From 79860eb88d98db1d84e20de0923d42463d999018 Mon Sep 17 00:00:00 2001 From: Tutorialwork Date: Tue, 30 Jul 2019 20:49:31 +0200 Subject: [PATCH] Release 2.3 --- .idea/artifacts/ProfessionalBansRel.xml | 8 + .idea/libraries/BungeeCord.xml | 9 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/workspace.xml | 160 ++++ ProfessionalBans Reloaded.iml | 12 + .../de/tutorialwork/commands/Ban.class | Bin 0 -> 7057 bytes .../de/tutorialwork/commands/Blacklist.class | Bin 0 -> 5304 bytes .../de/tutorialwork/commands/Chatlog.class | Bin 0 -> 3044 bytes .../de/tutorialwork/commands/Check.class | Bin 0 -> 4644 bytes .../de/tutorialwork/commands/IPBan.class | Bin 0 -> 5757 bytes .../de/tutorialwork/commands/Kick.class | Bin 0 -> 3891 bytes .../commands/ProfessionalBans.class | Bin 0 -> 1303 bytes .../de/tutorialwork/commands/Report.class | Bin 0 -> 4362 bytes .../de/tutorialwork/commands/Reports.class | Bin 0 -> 5054 bytes .../tutorialwork/commands/SupportChat.class | Bin 0 -> 6926 bytes .../de/tutorialwork/commands/Unban.class | Bin 0 -> 3608 bytes .../de/tutorialwork/commands/WebAccount.class | Bin 0 -> 4623 bytes .../de/tutorialwork/commands/WebVerify.class | Bin 0 -> 2228 bytes .../de/tutorialwork/listener/Chat.class | Bin 0 -> 9194 bytes .../de/tutorialwork/listener/Login.class | Bin 0 -> 6263 bytes .../de/tutorialwork/listener/Quit.class | Bin 0 -> 1939 bytes .../de/tutorialwork/main/Main$1.class | Bin 0 -> 2894 bytes .../de/tutorialwork/main/Main.class | Bin 0 -> 19263 bytes .../de/tutorialwork/utils/BCrypt.class | Bin 0 -> 21902 bytes .../de/tutorialwork/utils/BanManager.class | Bin 0 -> 15749 bytes .../de/tutorialwork/utils/IPManager.class | Bin 0 -> 6383 bytes .../de/tutorialwork/utils/LogManager.class | Bin 0 -> 1109 bytes .../utils/Metrics$AdvancedBarChart.class | Bin 0 -> 2542 bytes .../utils/Metrics$AdvancedPie.class | Bin 0 -> 2305 bytes .../utils/Metrics$CustomChart.class | Bin 0 -> 2151 bytes .../utils/Metrics$DrilldownPie.class | Bin 0 -> 2731 bytes .../utils/Metrics$MultiLineChart.class | Bin 0 -> 2314 bytes .../utils/Metrics$SimpleBarChart.class | Bin 0 -> 2342 bytes .../utils/Metrics$SimplePie.class | Bin 0 -> 1397 bytes .../utils/Metrics$SingleLineChart.class | Bin 0 -> 1461 bytes .../de/tutorialwork/utils/Metrics.class | Bin 0 -> 15232 bytes .../de/tutorialwork/utils/MySQLConnect.class | Bin 0 -> 2804 bytes .../de/tutorialwork/utils/RandomString.class | Bin 0 -> 1082 bytes .../de/tutorialwork/utils/UUIDFetcher.class | Bin 0 -> 2737 bytes .../ProfessionalBans Reloaded/plugin.yml | 4 + src/de/tutorialwork/commands/Ban.java | 193 +++++ src/de/tutorialwork/commands/Blacklist.java | 130 +++ src/de/tutorialwork/commands/Chatlog.java | 58 ++ src/de/tutorialwork/commands/Check.java | 142 ++++ src/de/tutorialwork/commands/IPBan.java | 111 +++ src/de/tutorialwork/commands/Kick.java | 80 ++ .../commands/ProfessionalBans.java | 24 + src/de/tutorialwork/commands/Report.java | 84 ++ src/de/tutorialwork/commands/Reports.java | 77 ++ src/de/tutorialwork/commands/SupportChat.java | 137 ++++ src/de/tutorialwork/commands/Unban.java | 95 +++ src/de/tutorialwork/commands/WebAccount.java | 93 +++ src/de/tutorialwork/commands/WebVerify.java | 46 ++ src/de/tutorialwork/listener/Chat.java | 204 +++++ src/de/tutorialwork/listener/Login.java | 135 ++++ src/de/tutorialwork/listener/Quit.java | 29 + src/de/tutorialwork/main/Main.java | 481 +++++++++++ src/de/tutorialwork/utils/BCrypt.java | 752 +++++++++++++++++ src/de/tutorialwork/utils/BanManager.java | 640 +++++++++++++++ src/de/tutorialwork/utils/IPManager.java | 250 ++++++ src/de/tutorialwork/utils/LogManager.java | 21 + src/de/tutorialwork/utils/Metrics.java | 754 ++++++++++++++++++ src/de/tutorialwork/utils/MySQLConnect.java | 71 ++ src/de/tutorialwork/utils/RandomString.java | 22 + src/de/tutorialwork/utils/UUIDFetcher.java | 48 ++ src/plugin.yml | 4 + 67 files changed, 4888 insertions(+) create mode 100644 .idea/artifacts/ProfessionalBansRel.xml create mode 100644 .idea/libraries/BungeeCord.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/workspace.xml create mode 100644 ProfessionalBans Reloaded.iml create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Ban.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Blacklist.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Chatlog.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Check.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/IPBan.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Kick.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/ProfessionalBans.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Report.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Reports.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/SupportChat.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Unban.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/WebAccount.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/WebVerify.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Chat.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Login.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Quit.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/main/Main$1.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/main/Main.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/BCrypt.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/BanManager.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/IPManager.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/LogManager.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedBarChart.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedPie.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$CustomChart.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$DrilldownPie.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$MultiLineChart.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$SimpleBarChart.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$SimplePie.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$SingleLineChart.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/MySQLConnect.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/RandomString.class create mode 100644 out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/UUIDFetcher.class create mode 100644 out/production/ProfessionalBans Reloaded/plugin.yml create mode 100644 src/de/tutorialwork/commands/Ban.java create mode 100644 src/de/tutorialwork/commands/Blacklist.java create mode 100644 src/de/tutorialwork/commands/Chatlog.java create mode 100644 src/de/tutorialwork/commands/Check.java create mode 100644 src/de/tutorialwork/commands/IPBan.java create mode 100644 src/de/tutorialwork/commands/Kick.java create mode 100644 src/de/tutorialwork/commands/ProfessionalBans.java create mode 100644 src/de/tutorialwork/commands/Report.java create mode 100644 src/de/tutorialwork/commands/Reports.java create mode 100644 src/de/tutorialwork/commands/SupportChat.java create mode 100644 src/de/tutorialwork/commands/Unban.java create mode 100644 src/de/tutorialwork/commands/WebAccount.java create mode 100644 src/de/tutorialwork/commands/WebVerify.java create mode 100644 src/de/tutorialwork/listener/Chat.java create mode 100644 src/de/tutorialwork/listener/Login.java create mode 100644 src/de/tutorialwork/listener/Quit.java create mode 100644 src/de/tutorialwork/main/Main.java create mode 100644 src/de/tutorialwork/utils/BCrypt.java create mode 100644 src/de/tutorialwork/utils/BanManager.java create mode 100644 src/de/tutorialwork/utils/IPManager.java create mode 100644 src/de/tutorialwork/utils/LogManager.java create mode 100644 src/de/tutorialwork/utils/Metrics.java create mode 100644 src/de/tutorialwork/utils/MySQLConnect.java create mode 100644 src/de/tutorialwork/utils/RandomString.java create mode 100644 src/de/tutorialwork/utils/UUIDFetcher.java create mode 100644 src/plugin.yml diff --git a/.idea/artifacts/ProfessionalBansRel.xml b/.idea/artifacts/ProfessionalBansRel.xml new file mode 100644 index 0000000..6e7710d --- /dev/null +++ b/.idea/artifacts/ProfessionalBansRel.xml @@ -0,0 +1,8 @@ + + + D:/Development/Netzwerk/Proxy/plugins + + + + + \ No newline at end of file diff --git a/.idea/libraries/BungeeCord.xml b/.idea/libraries/BungeeCord.xml new file mode 100644 index 0000000..f227554 --- /dev/null +++ b/.idea/libraries/BungeeCord.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e208459 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..c547529 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..0c811c6 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + BanManager.ban + BanManager.mute + UPDATE + set + YOUR + change + §k + LogManager + + + §o + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1556805016680 + + + + + + + + + ProfessionalBansRel + + + + + + + + No facets are configured + + + + + + + + + + + + + + + 1.8 + + + + + + + + ProfessionalBans Reloaded + + + + + + + + BungeeCord + + + + + + + + \ No newline at end of file diff --git a/ProfessionalBans Reloaded.iml b/ProfessionalBans Reloaded.iml new file mode 100644 index 0000000..d674031 --- /dev/null +++ b/ProfessionalBans Reloaded.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Ban.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Ban.class new file mode 100644 index 0000000000000000000000000000000000000000..99ace0b6d1020b4bd73b12fc54c07ea9bfda9c70 GIT binary patch literal 7057 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 literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Blacklist.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Blacklist.class new file mode 100644 index 0000000000000000000000000000000000000000..5d4f166a86ee6b1d234199e133a2ee3e8b7dcadc GIT binary patch literal 5304 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 WV``JSk(SoRg|tC#_LFf5k^cZ13fcMq literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/IPBan.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/IPBan.class new file mode 100644 index 0000000000000000000000000000000000000000..5f99bd2aa0da72cff05f1b3d9e629d4c0c23be9d GIT binary patch literal 5757 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 R)QuvpV}xikYLINr=r01eSz7=A literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Report.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Report.class new file mode 100644 index 0000000000000000000000000000000000000000..6635a35ab3a82cf3d55de962c1d7becf7a936ff6 GIT binary patch literal 4362 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 literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Reports.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Reports.class new file mode 100644 index 0000000000000000000000000000000000000000..6740406dc8cafbe1505f18e401c1c6db4211e84e GIT binary patch literal 5054 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 JzeCgH`@jCT${_#% literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Unban.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/Unban.class new file mode 100644 index 0000000000000000000000000000000000000000..7b25946736931beb7e1bde52d5e775d774f6ce81 GIT binary patch literal 3608 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 literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/WebVerify.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/commands/WebVerify.class new file mode 100644 index 0000000000000000000000000000000000000000..c92ec163d467775e249b86bd0525927a13659a70 GIT binary patch literal 2228 zcma)7TXPge6#jY>m}N6SNPv)Nk_m}1A(v4Efh0zALxV|(2?+u!^v?Fqb~4+u%mo6z z`yb+yR#}y2eF&fyCGpJ{%YWcM@L8oOPtOvncC)Da(9_-L+TVB1*KYfT;W-dZkpzNhkL6$kox@mvOX%vkfCQTC+sMWd=h$h{b1d8{9=)- zTxeev*;Nt9{Jl|zI;WV^j`jp*FvNy%;)vp0=$lg_vWvN!XYFjPTp?^;lJ->D;j&OU z5scp0>mx~iWo34nft47BYkF~frGyhnU)hU+zad3#(c|~XAt4hct{J*(T2{svi52viSjAO>-M%ZbcM>YBu`B+%P~#>(#x)by(Qe`fJ~8mAfzM3b#4QsX zw+t+q$igv^gD{bYYoegD9N*dEJ6qmNkoP?|5Yj1hSvyb zS|@d3CDkPFOA(M>$W=bzuCVU-%F4;Waz#F-3Q-LGI!0_2IC)Ggl{Miweyk!(7A-_^bMI-H=fy&` zpN{8mC!m2(BDL zt%GoSxi-bmkouIbLSGMv*J^0*9fS;B)h_oap$l#FU!#91Fg<8!?V!^*om2GPe`??X z*tdzilh#afYhoV`!lLu>vKIL{Nhi`rH+skiC5hg$FZLV#VeJE0=fD%BHu2ZMPe?5c zKK>G2LqDMI+5^;ohke`F-(=9Tjr1cNNMQ@V4n0DKVH*u2^{w>}VQ%7U{XMve&j;&H zZ{tv7>sMIPSBH~x<0g9b=}48^rV6(kRnq1P>Ggk=j#WvIR@in_Nsm=XyC35CHd-oQ zd}3FnH3bcF9W=<7&_wGI81yyKOuM773F;BF(0}>~lDE?IZi7SA6^OJih_Y{pf*peX zcf5|jaSG2cz&bFLkaULNAHfrn^q`(R{((O9Q!JunmpIy;!T=Gb?*_>(g<;C!IMPpH a8?c{Z)xaC=?d^Y}E!9bSZ;~{H)BgYtl2b4M literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Chat.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Chat.class new file mode 100644 index 0000000000000000000000000000000000000000..07218bcbc1acf7d9c6cbe7bfa667c6ce2ffc7597 GIT binary patch literal 9194 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 qA5W6stEk=v{D4$gyGRkiYkvoeuhowL literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Quit.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/listener/Quit.class new file mode 100644 index 0000000000000000000000000000000000000000..60e7bbe8358b64a0c59a2f6e31b83d8258ad46b5 GIT binary patch literal 1939 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 T^z={&o!CHcG8wC;C;k2h+o2Z= literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/main/Main.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/main/Main.class new file mode 100644 index 0000000000000000000000000000000000000000..0f3026835a7db05f992d713768ccfd9116a82f4e GIT binary patch literal 19263 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

W8SE7{KN3dRGw6R}{E5$gs^VhQ{G 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 literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/IPManager.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/IPManager.class new file mode 100644 index 0000000000000000000000000000000000000000..4a1075cc3f2527a888ff4e06748b6b3b6fcfd81e GIT binary patch literal 6383 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 Y#h8}ze{)(1GGUe&IW6HD-MVZ41N29Hh5!Hn literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/LogManager.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/LogManager.class new file mode 100644 index 0000000000000000000000000000000000000000..af3d416de9d3eb702196251fc3134957fc95dc68 GIT binary patch literal 1109 zcmbVLe^1j;6g_Vn>ljs_f{3CNMAwZ$Rq*G~WI#=Z-4TfuGQ?L_%Za4 zVKngr_@RvVZ4d}UP3+Qp&%JN&-gDl&{rT(L4*=_Up2lU&k3z+QgvB)Cm`~xh@JlJ& zN#iauBG6LE3ZF}38F>jS5>^?K8;<9MFBsxlw$6~)@|&Dt+;lu%iQ4--u9@+ZXSaAzU>Iuz+zz?ng+Z6$ zm1Z8;A8g&Vy_Q}J1IKF>{x8#Ky}Mi57959alEBUGU^Q86gpTi##)==3qIAuVf(C!- z2zleDTvtUA8RM9cpv$<2`wTPZ3vWh_+vEX5p;W0EZ!EP`v8t-;w=~f#uWs%Qd~wSv zRV#V5Qnie{x?Qx4tXeOcyGBjbGBO@ujre5pYDU7kjE8t6a*r8enXHT_cq-wUi~=?o z)PJ2Z!^A1e>iz+5gbcIiceQQD)5~NSM%vxZdzWG957$@j)?S-izUNUTy1=A;c*wmb z!^#DBmmNC#;}fEdXK2;BoshQ~CL2)@a4)o+HZMD_E2fABaED<|+xc6qROk<;g<(n? z{v&l;Jg;~#i)Y1MkHz|tY)^BpA B3*`U+ literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedBarChart.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedBarChart.class new file mode 100644 index 0000000000000000000000000000000000000000..125f99a44434c1d1b59f51227b660b964f454d85 GIT binary patch literal 2542 zcmb7G+jA3D82_D{f-uv+Sc?Pi-UO?KmEQ(AB6 z4F3R+IzI7&PY%u~P&?CMbjBI=L4EKt4&rzjbsY7X2c>>zlQxY8l$m6|bH498-~D%X zKf3^9zEH^e#Qs`p{CC#*@xdn%`58PaP&noedmMd*Dnw5?vUB|T2edF=|zJ4+q*kf9z z+t0A8t$1d&(kd&}LTVpn@DJE2il;7NTE=i8J7G9Si}^GrY)Woab4C+Z3U02v%ad0y^f>Jk%=0b8)uH_V39^v zwULIQ)nk*MKjnqAp{lW!fC4iz5OV6vYB|~H@|cn_GNaIhCa29@&Y;q>V+@^@Dn)-c z7(s@umD)GdTa$D(eVSZ)qeSo|Pzt08ejb>}uQE8R8#&jst-Qb#?UQ!F(TxM9gqK>H zoNgJuicK(8tj7k1<{P&|s+|y+R*^xLT2qm^3JbQ19G(?$ROI1OG%5;`IV08AR;U-N zpyH$y@p0;Ew3=hDhYT&%WJeq`Yr5u)Auy|A4$sN>o>%b#UR3dtv|I1FT!mmMJSFfl zL-(!WiJ|@G2L=jx*Upx}8?M+jmJ^S=hNBUPm#g80w$dhsNEPf!)cD%uv!!gP%JoA7 zEN@SSb%d#M;wytI%=}E#O9HR#%+m>)E3r3Ks4wS4 zXT+42A2w#mqTljJkkDQNZbLbwxJ8gQs0hNt(&0(EjoRX?gDz>1$%={;FVsG-B3g>z zwYjY-SO+skma_8{0~Ep`gRl_XB5BF#2M#Rc4U zZs~Ny??u(wczYKyI?q!&ey?yNSVx8X96|mC>iL&w;a{PHe~m7F1u^~&M)*}6=ifr- z-(iw}51aphGyF%KCHe;c377cKSm3|lGXE9t@!#+v|J^J82!c36-zwdRQMqlzu>*HQ z(09%xcG722Ko+#rnjqfCz38Dv)MEj=X(b!+Cho)i)WR5EM=$y)UfHfDk`-<2Asa=l z2S}@*WP=D^M~A?J-tQsy7wVY6-aoNTVBZprQdnT1EG7~01@;TnG8&6(XuXEU0GW}+ kq3F%E&EzIZW4IL|nuh@H351BJW6~e`8wrw`>;M1& literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedPie.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$AdvancedPie.class new file mode 100644 index 0000000000000000000000000000000000000000..56d7f7150b3204c97cd6c6474b54cbbbb24acc65 GIT binary patch literal 2305 zcmbVO-BTM?6#w0u&2B!HuK;Pm(xRe;0Nq+EDj>E^siqASNd;R!m(4XSYT8f;-ZF2Da4RZ<1*$oTuDR4YjSx#jRLNwaScVeENECvL&tRuODS~X4S87BP||Qi zI(RdIvy!f8cuT`ghJI&D^ujB#v* zLCJe%3b3nayJE3XT@(I|D8Wq8qik19-+KN-Ivy;NKxSAJP8y z3XgA9DO;$Mu=iunwyE#sL^j&GSzi>l$)nxZ|k>-~_Aa?Tslwy-E zv2jV+ON!bOtOG_JlS{+b#&>{Do z+vxfl>b>3HVpGb5WOqw;&%Q!t8@=~-f9zL6@8g-iiEZ?c->0yY5P(bQCeY$Ya24HL z!w^p($5WW#It<={W!{N3-UY#Xu+B61g!keu@584&i_iFRe8C4o(B|kT!ZOZa9Fw%8 zq?_<1@Hue}0;?N$ah7(RDzk;>aE{~evWU-2;kffrim{60{H>w{SC{Bj} literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$CustomChart.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$CustomChart.class new file mode 100644 index 0000000000000000000000000000000000000000..0bf861e2380c2d3e20d65a3f15945f5ff2fe31ea GIT binary patch literal 2151 zcmb7FYf}?f7=BK&uwhvWL=a*@7K$YoFi7o1u(TM#Vgo3lDE7K+j$vWhO?NkfKgD0r znSP>5p&-1=#AOG|HPXNxsQqY6<74%|8!3AVw zT#R5=frPmhy169d0|gPxL=Y46yb!+}K|*Xkl#y-00v2T~Dd-eJSHyf(49f~~SdpH}zY3+S2VpI_JBlU6^8MK5yElKh4k`KYE>5WsovXo-?#(O`9*4 zw%574qOV(Y63se>ZmsIBDfZPv$=@(NhSp5U^PTOCaLu63+ctMImhO3+E>qb&Px~d` zAx&$?aktVX-?Y5+0;ix1Z@5mE0&L6qy0NvO7XuGLR@WK23fy1jUzWJ%U-le(X?>F$ z6$7zlJdH*GxWV!ijkM@I;2)M zTxUlBAg~l3xyx{(BG4!bhE9^2(@l%#s|gS!>;^+qKoqeqV~yw~*eJ&U!HYp)C*v;TNW?q){I*yZFAc{%#1+`Aeql=hHl%AudQ>< zE?Ji5xEkLs`nLy#(LNP1oFF2e&$x=u@VSaN@s^4&K*S%bc|Rmo#<~gv)I2@4+e+_T zD4AAXkUd%lC>w&PE!%5p}&# zeN=>fQr*d6RRwAGzmRfikY4*<`cBY0 zD@vJG68)*N>7<`lw-H8&=7HosLaB#PQe}kipzQ$CTC`~&%@2~P`;fmOkuXlucpOb+ z?H(j{AMLD+82c6{*>@NS?gCTL@EYyNXyB4znARN#JwZD{GET{O9cUr9)Vh^0Sd79V zyT~0NvbK+w#j&W8ya&0A)?^v#q!g1Lp^5E{McZOh8LxbW_@kw; zH;l4}7-x^L#D2$B_D7KBG@1$1b|lD_gi)MAibjEJ?lAQn#&DXF6K83RQw;RA#Tn8F z2XMq8{fz+`6ZCrm`dSCLyuoM-8qWTOVH*DglO*xBkfpcDmPGSA9TA+vWCb|PlD#n9 z$jMaSKG+^DLjh0C!a4go2w1C#V5-WCjPuptWV{;$7s7O)`5uO7R!Fx8A7M~x`4@9c B9u)up literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$DrilldownPie.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$DrilldownPie.class new file mode 100644 index 0000000000000000000000000000000000000000..7fd34bede1097cb8a5c8e49bcb67206e1fb5cf2b GIT binary patch literal 2731 zcmcIm?Nd}$82_EKm%Vp)uYxETCfW-rEH9f#q<|;_8n_^_Mk%IUc2CQdyZ3H)?}GFX z^fABYKj1Vqlg*e6H8p;!89&t2r>4K8aVF_GXBXHdKx?J}&dYP2=l8rnm&Z@P{Q*D^ zmZE6HNCXWS)o~$;5H7~>7RGdp#}LLPxg=wlz+?=UF(sF`bxg-#A|>Ch=y*q7W^}wO zrQegwRRbq=%<8z#V5BX_vF04Xa5Q<#TC@_yz;+U8&rKJ7U${YHxXjSc5J}lt*9wY0 zaeur!_dvz-gq1H}9m~xoQh{%~+5YEbP$FY)AhN>mA5=dK;Q`yVgF%L#))LGPz}vae zHp8F|dl~YfHfg(JvY49_{*_V;>XRM?WXAGsc`p;SVBRh;m?OUJI2murow7;T9CKaa z4?9+&An3ItnGuPg7^-T#N-vN>su5@a(b&;(L(Pt9%H-(nw8#gx=N5F_qSjbX+Ecd+qq%%Qy-8@)NrvjA zln5C1txKp{A`+EfMUE7YVQ<*pSjE2X6UpfFR$)@yCPd9uTbM@3B_UPPf#F!Cdu7A6 zwbdYY)#DUPE0dY>y}aBlP(s=hZWl%+KHtB{c#L-%(4+tul)BXSfFCEC1es_`;? zv>WLoP7_`hKJ86(<2Xcrj0*iQ?af3<(;lKd7H?kxi+6kj{xw}gI6~`Q`NZ2%!#fb= zoj6LgmrCrkze+a}LCDq4?){3^0V!n@XV#N8!cL-fwftD=q z6Y&S&tEgdkgz#kNk1#t|p)-6&r2AU0MmK|D6%mHMnx>jnMCIEj@)VOYW?#6B)Te8` zMrTua6?>$>UM>+Uq$5p{vUHa`m8GLidQ()1JVLEJ)k)NS4^bcLHSVqb++f^AW2jf_ zC<99_{UP?>TPrqb%Eo72lB2Lh2SVp^?Hv_++oA_w{;4x0pNtVO+^grJll$Qj)KrhK^ zDwi_jB@J(qh7#H-LM!o~rq@0~9)j@{aUK2YcYytcTBhU78qOf9V{r3Ho9KmR0(hwPQp3WS@c!l;uq@n@WsHP)SSYfn>BUIbh@V*xL8y;QE3;+NC literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$MultiLineChart.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$MultiLineChart.class new file mode 100644 index 0000000000000000000000000000000000000000..ffc1353a06175743dca1c8d967cd9a9b5c1b46f5 GIT binary patch literal 2314 zcmbVOYfl_i7=F&#VP-GGwS@|n78NZl3sY-F1;pAd)omAA*$TE^huPyYFgvr(&SIOW zF``jhqc)8u`Um>8CQ5@E<0n7(6Z|J8+WMZE<+4giYgo>E-uwOR{(kGnUjQ6MkiZoJ7C8H4dwp$vH1VT#A4Ma)!2j>`+ ztXHG}Tl2OnW@_aH;h&2V?8tkR?VRb`60Z}LV9~BJv`p8Wz?Lf6Mbi%$^qlJoKkJy) zsv!PIz9@{K7I>uWyzTkRVV$bRw4iLPYJa0TWuaNpPOiw%@!u>Mx*B7-RN08fx#y(+ zcd2Eu^pXg|j8B+>$#7s`FmHKfqvUxdM;N85=NhNzIJ2-Mtbo#LqLJ_~5uA!(XJeoH zLo&%J#n9gV-L0uBrc;yUjg`%cY}wWX($J8ZBn@9-*{)Os6_Q8>DZ4~ zb=-$;hMqguS&F@;;dLEvfSS_WP!$~uuyhn5G`y*!ghh%&hb@UqQhZN?cvK-B%aYX| zvbOJ>4hLl&uH?3DaieW(AVsUB!$U>KTkthd-!k{I6%2#Bi)U-qz$@1mFx7x{Y$cTo zgm2O!Ze$v!v)KZM&K)*Lj(TiBp1oU!c6j7yaOH!@u$LBLs~nr-ui4e9awVVzAZs*^ z@o*gqf*y?a4UO<|A`*@7w$6nzaeFiNt_JaqoJdDoG^;b>B3V@25X5Ok=j5J{JKNp3 zlK&jNmBc$OD4dj8ES~kfituG{hju8zwsfLaj)z4dJ%%GY$?r6uQ;sN8%AsoSuFb>) z^u4u@zQ^bRlmkz&hcJ%&>7UVn_Yyon6pLVtU@ARy6D*zi0sK2cV>n1>i)8V4(8S+G zlD~&OqV+}MN=X` zy=4up-$K29`;XYDl9A*#Np9a1=vc#^>$iXIR6^@R9eXot=p4F1VJTq&j-!nV@B!j{ z6>a=O^zx68<{u-&KZU_BW0qgR0{;wxe~v}|1>WahVwGRTC;S>N^RIA~e;pQWl71`9 zVgy4NrW++)FJBy2h-y%=+OUelbmP>S6+DU~R33w#naAjth!U2xi+GWZ$H_({HA+%X zki9;rw~*HGWcYcC{f$>&$Xn8$-iP}5Xo N)CA4VJT54Se*n;LTFU?c literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$SimpleBarChart.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics$SimpleBarChart.class new file mode 100644 index 0000000000000000000000000000000000000000..c8a11b6916dee3a3e50e97f493f5316fc114e2bd GIT binary patch literal 2342 zcmbVOZC4vb6nwEwDJ@87)1qR95QtK&3W(A`D{X*kDuw#GY{sy#*^QeG7C(57 ze)9Ma`~@EMgXpOs#pCglAN)xkPxa1hcnPN1dXntS-22=+_ujeBZ2ov~|5pH`@Z(5g zDu!-c(U6NHgy{rka8<*#1R{7{JZ2NPjvEQ&F()4L8WuF%jH3n98s11i$D0}oakSx< z5WXcOiyCeVZ%a{(YPh4}ZHB06*tW4^bA}7~HDklb)_lv(nyzEkJdZnmHdhCXGsFs3 z$uazzN9fxpqbH82=Zs2yX&X){Tkt*0DUAmJDVcSAUgF;PBz+hn6P9E7lMJK%M=%Xm z4~=nPkwMM5MGCMjZ#jIvR$k%WtsudUyi3_G8lENAb)o97T2+SDf>o~A{EFe_Rt?W* z&}SWodpX;vRypa1^F^NZYrady_Q$TbF6&gy&T-1dtoAjUQx=*j{LB^^I{uvnLsw%g zo0a`|?8i>}f0tSmOD}O>W;|{92E+OOfxPLKvnAIp**sgSx=!{wJr-8hxam_`t)@X| z<$9ZohFuedIolLJ<$1;?!Nb%+-X@JR!Pwc@7JX7oIJGFZKiAaR!RC>T`lg|>Q4uG6 z*s#=N!G zYM{*H5Q|~pWO%Mt_1$uv!-XZ7kheQ@151h&INdp?_ATW7Mj~qIBPT2|QR^V~t_Jvi zPGqC48r6AzmprPD9HPW>N!Vkl1*bJGQ!|QmSM~NcW#Ej>%M_*rM#$F2N76g`u;b>g@S=>mlAEi@Z7v}J86pio3n3YCH?!JF$?sawa6>~W!A!83nR zB%lH$#1jvE1@Hg}!6$&YI8)>+H3_#DG@`XHdW$C=DBgH&+K6uhHoIp&?9 zx?+0w>g1C}moiz_Jxj<9rNmb)Kcro)+2)c(mg<7zm|nrwecz;`OtEZgJm4PhKP+zjcdCaLPo%F_OkNNyZs=TR z7)WP|hFjGtu3NE9t>U|mcAI{8SJq5}6J@C!;`*ASS~he$pxU&RxNfX3>b0n=3b$l7;NiMm8XP7rFF*9nTP!nQwRCMBk3KeY(slW3Qu!}M-skjW{*cndU zo?U=?UHVA(8iZX(?r227dgwk#=*K`K@MH2L0_R>MdVd>IG5ZXWmlO(b(Yly_ z2POXk@)J^Q<&rVl$8%c*uM??-YH;l0F$gm%KNah+OV;IFC5vRtY wmnxGY3L_!O$WIK(7?m-`aFR3rN9v6#iyg*j^TqtB9otqVxi+7rYkmQZT70Vp2n5q9)tzP!^WmW_K3l8GHku z0uu!x%MV==~aes-mQ@L?xN-J zr@>a4d(Ru}dy6iGvTAsil$(cPv2OW;Ryz<;5gjN+lKFR0_epG*F_*) z(zf5Y-ex@uzP`+fnCVaarB89FrZiNjF!cT#2gBfTHaoQgMuk&{eR3vcF0?8a^>`Ky zVK5|9>7waw=~dUIVD+l+I{Fj(t&}&oDTs5pQpY9QqdK+?JD?P^0z+3x80O}(QLE>Z zx{4vT^w#7xVY!a4;tt6rH}FjU*pe-zH4`=?J4r(i?rP}58HVBi6O*ueDsmd;Fwf9a zZ^G8T(XfE~8Xn-GhDXS&SY((!RlOL}?eY1*7w%TGH{!>nz9T+|kuwaOP)1pjP)hzs z+71#GLJl26KNalA`eB(OmLpahH6U|T1+`w0l{IRsRH0SnV6YhCMy2x1b8FlaI}DR2 zuxs(un49`IjkL`R#^^E8=y9PgmvyAIn7!E7se(Q_F=-9a8qZ|+!7@{ypnM=P zgns&;kypw~bSSS7Q(j|$&Mr3K7{qy!ND;%hKqlo*I#Q6Jy>@sa9X4E#AdDnNn~vX+ z9qBmp4WSqN2p2OSp?pCH!w+=sL7h*$Ct)|+8%cadbPuuYE}0F!i;qP>DNfl<#= PoF3?N1lDN19ghA1@C$J) literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/Metrics.class new file mode 100644 index 0000000000000000000000000000000000000000..a551b10ffb9be4cf23be057d3c885969b007b5a0 GIT binary patch literal 15232 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 literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/MySQLConnect.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/MySQLConnect.class new file mode 100644 index 0000000000000000000000000000000000000000..a18afe4488659e6aa0c219ea94c22114b52eef36 GIT binary patch literal 2804 zcmbVNTU!%X6kU@9CWJwdJBk{qR0v?KpjOl>H!n2-BwT83YbVJ829imbnJBc?-tV^m zp-;S4Xi?hl>w^z{=zr+%=v&)$CLu(?wqGA+pX=Ux?X~yp^V^?4{Q{s5-v$uGwF(@; zbtztz;)WEF07X>AXaFj%`7tKPaX)VQF(Ie1046adZ%zj=gV&_ERe{%W+mAOY@h09< zamSCh189I=i8vBcBr72>D@CFLbC?gnz=B*Zsz|9wEAU+#i%lx1jV$PQ^>9iz=fW|^ zGR(R23jD(ZlLLbTu@PRIij7QgXM7+Qn;DxJR^Ux!Oa%>jJ$oq?9?F=eNH|8uq-yyE z!!(>%6x`_8ZTbK0bWRh-P$nr9R7VU`jONmDVNL4s6nAPPnS`F2)-6M>3!6S?-mn!M zizG$Z$vGJwrtW2|#c<9sQg%4H61y2GpeAJBe8zSdVp4bXxNg&^FJ}vjJ6YYfsh3os zGH4kj!|#xI6y9purhxWFmJ=ddAfYPe=!wOso^|msKMKkcsf;vTp3Bk?Ck=b>BQ1L* zmiiLY4zH($=@6-R$y%YQrJRss$6gk;6(@w9OF1zi6+IT|C^5Qy=9Yu0BBLM>%jB$t zxME0N_m`4%Le_|e4)kd_iZ%@|NO4Sxb}4$%M>=*E(qPU=B_)ql3(0t*KfS_Y=?{lB zWU)jd`g?m%o(U_cyQDKk6QaQIMaRks4Hj$_j)olWYIqUHRov6Cj1>*<;9V8(X}FIz z1y`T0>cfTzP75n;n8}AN|8Lsc>-2GQ8Xe|00wv4;Nis~kLkhL%;ob868 zAi9U^-3E8>6uj>0KL3HLcR)&u+7&X62=nd`n7eAYa7iWh>MQfNsiu$HsGn zPbg4^3fY7>tB`%j9odT9k==-+M>Y(OWnhy;CuNAMhaAf|wuK%;3C(n^!xNh6UWYd{ z)3Xj=Xr{%tj->LVBP1g03T7c%+- zD5nhK6l0xWyPqQRX>xv_U0+7tNYvA0`ZUhCx(^B*oaI>sJbz$H#mmQe#C}I9lOQgVJ?%RHl6mt=$_qltyKg z%TSu>;vl1LW!yoWB?ISNL?PxefPJ`ti}cxwN|zZKZbz{?I`9fvRHy|;<+)9`m0VuJ z=vC)Lz8eUcl<1)&TWAE^21|FPikK7*a8;(w+1k?~fqG6F>5MQjxWK0HznKbzQ=+ z^i_8+tvj1)+N4N0VQZ?HQXND1m7tI3i~>V5j_jw#G=j_aYX|U|?|uzh=7@XYdr#3@zs}=Uv0h=#HSBN7+g9FXHbk#Mgtldn*SYV<$8wB|MOzFKQuftIyOErIW?`OwT%8En=^KH z%{|M`zjO-R-9I=ydKI0IEi5iQSdKq@lvr6^TYtQf+ zk9I+UfR|gF{?no%az1ee9rVa+=?i$!iaOMTHc|5Y z#h(Oq1M6ifZ5miMBw6McB=0799^Cp3X6^6A literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/UUIDFetcher.class b/out/production/ProfessionalBans Reloaded/de/tutorialwork/utils/UUIDFetcher.class new file mode 100644 index 0000000000000000000000000000000000000000..e951ea6babe1c39501c94b423aa0efa8fdf7c9f6 GIT binary patch literal 2737 zcmai0S#uOs6#i~9nVxhyAtZ#P35y{t36M01C=4;kDg(nJNr*uOJCn|&!=!uYg~ffr z9XDK|tn$(F4bhrZnJUXCAAI%AXG<)r{0riDXGtc^s8coP-h0lu_nv#c@1DLt|NZ?B z03N_i6+IZO$3{G^Vm-#BIHTYR1y3q?N`->a7|zP}oD@&1h{BX&TtP}j6DAZ`Dw;7V zMOwj>idKwDVXK(N`4}=PX5h#HXV<}%A}hrO1)hpkFk|p#8%RN)^#~PQRB%bbWjPvE za7Dp00`*~NPwY2SQUl z3Ow6M@2=Dga-Haa?byMfKy6R&X@SUocY-S}N!pHeJe(P~ykT=ZLrqK4O_`a~rf17> zQ5gxQY+s-|Ibj(=7`QZMF1gljBU4i?NZXm@;CU}aUW6XVlXDv(ZQ)~58w!}-9=A}bt#ZV|~JfmrBUo@34szs8}! zOr0lp^3aud|17ypTJr)Zc#i%9sZ5a$>W~|HDeI6e!EY`@vR|@MLm&Dz^kS2S=kbDu z7x5C^@~;XS-h8rUofAIQ^R)9@j#YxoEs%ibrYjmnZy*ib<9Y4{YMY4{vBc)LsS zBu*T>oU*ck?K%Q$7V?zHWUREAA$);V=CL&IQC<`Ty8ibXF2blls`3yPE>TIFkH2i& zhLr0BrtSDgtt-5L#20)MOYMJX+_pK)#Z+bdO=o}<}T(9r*{+sy2l_ZW$S{eSTCeDCaa1po&t4Q zNg07{RS#8-V+*yQ^FPe2&P?xKUsjJ2gsXXiYE$M@T$R(M+4X)n?&m)eGGU3TiHv3} zCml=?+n(M8+1-=)7e8{t@`!$4PofO#QtB&I^Chd61zccfd9sSpm(3|^$|EeQ(8Vu% z55M%pR@No9n>nh%7SyuOaz$xSW3Ux$>o(0oe3N(CN*T>78?lXjt>A$Bv7MuOJ|1>Z z!^t%p5kVt$f8IvTrW|T-psCP{{D`_+bBK=3K^dDxtff8&l^$vi4YSaan{#M9HixFM zEwfm1d=AZHeY02^kBE3(aYj6II^Li+G{;)jNmFZqaO9yp2 z^Rxnk=)_^H#8JL+hOrvwu!b+9E?mG`7Rqkc>2=TGKI#vfFaQCd~Tu$?O<(7-4fafCWeqRch-uubyche+F9 zuCFLKrr@}O6ADf$cq~6W1T~ahu_J#&t7b`D>WNdSbZYThvJ2@ta3k;N23A KMmVa$>3;y63d1V^ literal 0 HcmV?d00001 diff --git a/out/production/ProfessionalBans Reloaded/plugin.yml b/out/production/ProfessionalBans Reloaded/plugin.yml new file mode 100644 index 0000000..200f415 --- /dev/null +++ b/out/production/ProfessionalBans Reloaded/plugin.yml @@ -0,0 +1,4 @@ +name: ProfessionalBans Reloaded +author: Tutorialwork +version: 2.3 +main: de.tutorialwork.main.Main \ No newline at end of file diff --git a/src/de/tutorialwork/commands/Ban.java b/src/de/tutorialwork/commands/Ban.java new file mode 100644 index 0000000..4dbeae6 --- /dev/null +++ b/src/de/tutorialwork/commands/Ban.java @@ -0,0 +1,193 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Ban extends Command { + public Ban(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.ban") || p.hasPermission("professionalbans.*")){ + if(args.length == 0 || args.length == 1){ + for(int zaehler = 1;zaehler < BanManager.countReasons()+1;zaehler++) { + if(BanManager.isBanReason(zaehler)){ + p.sendMessage("§7"+zaehler+" §8| §e"+BanManager.getReasonByID(zaehler)); + } else { + p.sendMessage("§7"+zaehler+" §8| §e"+BanManager.getReasonByID(zaehler)+" §8(§cMUTE§8)"); + } + } + p.sendMessage(Main.Prefix+"/ban "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + int ID = Integer.valueOf(args[1]); + if(BanManager.playerExists(UUID)){ + if(BanManager.isWebaccountAdmin(UUID)){ + p.sendMessage(Main.Prefix+"§cDiesen Spieler kannst du nicht bannen/muten"); + return; + } + if(BanManager.getReasonByID(ID) != null){ + BanManager.setReasonBans(ID, BanManager.getReasonBans(ID) + 1); + if(BanManager.isBanReason(ID)){ + if(BanManager.hasExtraPerms(ID)){ + if(!p.hasPermission(BanManager.getExtraPerms(ID))){ + p.sendMessage(Main.Prefix+"§cDu hast keine Berechtigung diesen Bangrund zu nutzen"); + return; + } + } + BanManager.ban(UUID, ID, p.getUniqueId().toString(), Main.increaseValue, Main.increaseBans); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "BAN", String.valueOf(ID)); + BanManager.setBans(UUID, BanManager.getBans(UUID) + 1); + BanManager.sendNotify("BAN", BanManager.getNameByUUID(UUID), p.getName(), BanManager.getReasonByID(ID)); + ProxiedPlayer banned = BungeeCord.getInstance().getPlayer(args[0]); + if(banned != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(BanManager.getRAWEnd(banned.getUniqueId().toString()) == -1L){ + banned.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.BAN").replace("%grund%", BanManager.getReasonByID(ID)))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPBAN"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + banned.disconnect(ChatColor.translateAlternateColorCodes('&', MSG)); + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + if(BanManager.hasExtraPerms(ID)){ + if(!p.hasPermission(BanManager.getExtraPerms(ID))){ + p.sendMessage(Main.Prefix+"§cDu hast keine Berechtigung diesen Mutegrund zu nutzen"); + return; + } + } + BanManager.mute(UUID, ID, p.getUniqueId().toString()); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "MUTE", String.valueOf(ID)); + BanManager.setMutes(UUID, BanManager.getMutes(UUID) + 1); + BanManager.sendNotify("MUTE", BanManager.getNameByUUID(UUID), p.getName(), BanManager.getReasonByID(ID)); + ProxiedPlayer banned = BungeeCord.getInstance().getPlayer(args[0]); + if(banned != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(BanManager.getRAWEnd(banned.getUniqueId().toString()) == -1L){ + banned.sendMessage(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.MUTE").replace("%grund%", BanManager.getReasonByID(ID)))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPMUTE"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + banned.sendMessage(ChatColor.translateAlternateColorCodes('&', MSG)); + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Grund existiert nicht"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + if(args.length == 0 || args.length == 1){ + for(int zaehler = 1;zaehler < BanManager.countReasons()+1;zaehler++) { + if(BanManager.isBanReason(zaehler)){ + BungeeCord.getInstance().getConsole().sendMessage("§7"+zaehler+" §8| §e"+BanManager.getReasonByID(zaehler)); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7"+zaehler+" §8| §e"+BanManager.getReasonByID(zaehler)+" §8(§cMUTE§8)"); + } + } + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/ban "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + int ID = Integer.valueOf(args[1]); + if(BanManager.playerExists(UUID)){ + if(BanManager.isWebaccountAdmin(UUID)){ + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDiesen Spieler kannst du nicht bannen/muten"); + return; + } + if(BanManager.getReasonByID(ID) != null){ + BanManager.setReasonBans(ID, BanManager.getReasonBans(ID) + 1); + if(BanManager.isBanReason(ID)){ + BanManager.ban(UUID, ID, "KONSOLE", Main.increaseValue, Main.increaseBans); + LogManager.createEntry(UUID, "KONSOLE", "BAN", String.valueOf(ID)); + BanManager.setBans(UUID, BanManager.getBans(UUID) + 1); + BanManager.sendNotify("BAN", BanManager.getNameByUUID(UUID), "KONSOLE", BanManager.getReasonByID(ID)); + ProxiedPlayer banned = BungeeCord.getInstance().getPlayer(args[0]); + if(banned != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(BanManager.getRAWEnd(banned.getUniqueId().toString()) == -1L){ + banned.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.BAN").replace("%grund%", BanManager.getReasonByID(ID)))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPBAN"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + banned.disconnect(ChatColor.translateAlternateColorCodes('&', MSG)); + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + BanManager.mute(UUID, ID, "KONSOLE"); + LogManager.createEntry(UUID, "KONSOLE", "MUTE", String.valueOf(ID)); + BanManager.setMutes(UUID, BanManager.getMutes(UUID) + 1); + BanManager.sendNotify("MUTE", BanManager.getNameByUUID(UUID), "KONSOLE", BanManager.getReasonByID(ID)); + ProxiedPlayer banned = BungeeCord.getInstance().getPlayer(args[0]); + if(banned != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(BanManager.getRAWEnd(banned.getUniqueId().toString()) == -1L){ + banned.sendMessage(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.MUTE").replace("%grund%", BanManager.getReasonByID(ID)))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPMUTE"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + banned.sendMessage(ChatColor.translateAlternateColorCodes('&', MSG)); + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Grund existiert nicht"); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } + } +} diff --git a/src/de/tutorialwork/commands/Blacklist.java b/src/de/tutorialwork/commands/Blacklist.java new file mode 100644 index 0000000..1dd6194 --- /dev/null +++ b/src/de/tutorialwork/commands/Blacklist.java @@ -0,0 +1,130 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.LogManager; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +public class Blacklist extends Command { + public Blacklist(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer) { + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.blacklist") || p.hasPermission("professionalbans.*")){ + if(args.length == 0 || args.length == 1){ + p.sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + p.sendMessage(Main.Prefix+"/blacklist "); + } else if(args.length == 2){ + File blacklist = new File(Main.main.getDataFolder(), "blacklist.yml"); + try { + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(blacklist); + + ArrayList tempblacklist = new ArrayList<>(); + + if(args[0].equalsIgnoreCase("add")){ + String Wort = args[1]; + for(String congigstr : cfg.getStringList("BLACKLIST")){ + tempblacklist.add(congigstr); + } + tempblacklist.add(Wort); + Main.blacklist.add(Wort); + cfg.set("BLACKLIST", tempblacklist); + p.sendMessage(Main.Prefix+"§e§l"+Wort+" §7wurde zur Blacklist hinzugefügt"); + LogManager.createEntry(null, p.getUniqueId().toString(), "ADD_WORD_BLACKLIST", Wort); + } else if(args[0].equalsIgnoreCase("del")){ + String Wort = args[1]; + if(Main.blacklist.contains(Wort)){ + for(String congigstr : cfg.getStringList("BLACKLIST")){ + tempblacklist.add(congigstr); + } + tempblacklist.remove(Wort); + Main.blacklist.remove(Wort); + cfg.set("BLACKLIST", tempblacklist); + p.sendMessage(Main.Prefix+"§e§l"+Wort+" §7wurde von der Blacklist entfernt"); + LogManager.createEntry(null, p.getUniqueId().toString(), "DEL_WORD_BLACKLIST", Wort); + } else { + p.sendMessage(Main.Prefix+"§cDieses Wort steht nicht auf der Blacklist"); + } + } else { + p.sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + p.sendMessage(Main.Prefix+"/blacklist "); + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(cfg, blacklist); + tempblacklist.clear(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + p.sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + p.sendMessage(Main.Prefix+"/blacklist "); + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + //KONSOLE + if(args.length == 0 || args.length == 1){ + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/blacklist "); + } else if(args.length == 2){ + File blacklist = new File(Main.main.getDataFolder(), "blacklist.yml"); + try { + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(blacklist); + + ArrayList tempblacklist = new ArrayList<>(); + + if(args[0].equalsIgnoreCase("add")){ + String Wort = args[1]; + for(String congigstr : cfg.getStringList("BLACKLIST")){ + tempblacklist.add(congigstr); + } + tempblacklist.add(Wort); + Main.blacklist.add(Wort); + cfg.set("BLACKLIST", tempblacklist); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§l"+Wort+" §7wurde zur Blacklist hinzugefügt"); + LogManager.createEntry(null, "KONSOLE", "ADD_WORD_BLACKLIST", Wort); + } else if(args[0].equalsIgnoreCase("del")){ + String Wort = args[1]; + if(Main.blacklist.contains(Wort)){ + for(String congigstr : cfg.getStringList("BLACKLIST")){ + tempblacklist.add(congigstr); + } + tempblacklist.remove(Wort); + Main.blacklist.remove(Wort); + cfg.set("BLACKLIST", tempblacklist); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§l"+Wort+" §7wurde von der Blacklist entfernt"); + LogManager.createEntry(null, "KONSOLE", "DEL_WORD_BLACKLIST", Wort); + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieses Wort steht nicht auf der Blacklist"); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/blacklist "); + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(cfg, blacklist); + tempblacklist.clear(); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"Derzeit sind §e§l"+Main.blacklist.size()+" Wörter §7auf der Blacklist"); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/blacklist "); + } + } + } +} diff --git a/src/de/tutorialwork/commands/Chatlog.java b/src/de/tutorialwork/commands/Chatlog.java new file mode 100644 index 0000000..dad96f4 --- /dev/null +++ b/src/de/tutorialwork/commands/Chatlog.java @@ -0,0 +1,58 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.listener.Chat; +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Chatlog extends Command { + public Chatlog(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(args.length == 0){ + p.sendMessage(Main.Prefix+"/chatlog "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(UUID != null){ + if(BanManager.playerExists(UUID)){ + if(Chat.hasMessages(UUID)){ + String ID = Chat.createChatlog(UUID, p.getUniqueId().toString()); + p.sendMessage(Main.Prefix+"Der Chatlog von §e§l"+BanManager.getNameByUUID(UUID)+" §7wurde erfolgreich erstellt"); + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + p.sendMessage(Main.Prefix+"Link: §e§l"+cfg.getString("CHATLOG.URL")+ID); + } catch (IOException e) { + e.printStackTrace(); + } + LogManager.createEntry(UUID, p.getUniqueId().toString(), "CREATE_CHATLOG", ID); + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat in der letzten Zeit keine Nachrichten verfasst"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler wurde nicht gefunden"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler wurde nicht gefunden"); + } + } + } else { + + } + } +} diff --git a/src/de/tutorialwork/commands/Check.java b/src/de/tutorialwork/commands/Check.java new file mode 100644 index 0000000..74fdc3d --- /dev/null +++ b/src/de/tutorialwork/commands/Check.java @@ -0,0 +1,142 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.IPManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +public class Check extends Command { + public Check(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.check") || p.hasPermission("professionalbans.*")){ + if(args.length == 0){ + p.sendMessage(Main.Prefix+"/check "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(IPBan.validate(args[0])){ + String IP = args[0]; + if(IPManager.IPExists(IP)){ + p.sendMessage("§8[]===================================[]"); + if(IPManager.getPlayerFromIP(IP) != null){ + p.sendMessage("§7Spieler: §e§l"+BanManager.getNameByUUID(IPManager.getPlayerFromIP(IP))); + } else { + p.sendMessage("§7Spieler: §c§lKeiner"); + } + if(IPManager.isBanned(IP)){ + p.sendMessage("§7IP-Ban: §c§lJa §8/ "+IPManager.getReasonString(IPManager.getIPFromPlayer(UUID))); + } else { + p.sendMessage("§7IP-Ban: §a§lNein"); + } + p.sendMessage("§7Bans: §e§l"+IPManager.getBans(IP)); + p.sendMessage("§7Zuletzt genutzt: §e§l"+BanManager.formatTimestamp(IPManager.getLastUseLong(IP))); + p.sendMessage("§8[]===================================[]"); + } else { + p.sendMessage(Main.Prefix+"§cZu dieser IP-Adresse sind keine Informationen verfügbar"); + } + } else { + if(BanManager.playerExists(UUID)){ + p.sendMessage("§8[]===================================[]"); + p.sendMessage("§7Spieler: §e§l"+BanManager.getNameByUUID(UUID)); + if(BanManager.isBanned(UUID)){ + p.sendMessage("§7Gebannt: §c§lJa §8/ "+BanManager.getReasonString(UUID)); + } else { + p.sendMessage("§7Gebannt: §a§lNein"); + } + if(BanManager.isMuted(UUID)){ + p.sendMessage("§7Gemutet: §c§lJa §8/ "+BanManager.getReasonString(UUID)); + } else { + p.sendMessage("§7Gemutet: §a§lNein"); + } + if(IPManager.isBanned(IPManager.getIPFromPlayer(UUID))){ + p.sendMessage("§7IP-Ban: §c§lJa §8/ "+IPManager.getReasonString(IPManager.getIPFromPlayer(UUID))); + } else { + p.sendMessage("§7IP-Ban: §a§lNein"); + } + p.sendMessage("§7Bans: §e§l"+BanManager.getBans(UUID)); + p.sendMessage("§7Mutes: §e§l"+BanManager.getMutes(UUID)); + if(BanManager.getLastLogin(UUID) != null){ + p.sendMessage("§7Letzter Login: §e§l"+BanManager.formatTimestamp(Long.valueOf(BanManager.getLastLogin(UUID)))); + } + if(BanManager.getFirstLogin(UUID) != null){ + p.sendMessage("§7Erster Login: §e§l"+BanManager.formatTimestamp(Long.valueOf(BanManager.getFirstLogin(UUID)))); + } + p.sendMessage("§8[]===================================[]"); + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + if(args.length == 0){ + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/check "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(IPBan.validate(args[0])){ + String IP = args[0]; + if(IPManager.IPExists(IP)){ + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + if(IPManager.getPlayerFromIP(IP) != null){ + BungeeCord.getInstance().getConsole().sendMessage("§7Spieler: §e§l"+BanManager.getNameByUUID(IPManager.getPlayerFromIP(IP))); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7Spieler: §c§lKeiner"); + } + if(IPManager.isBanned(IP)){ + BungeeCord.getInstance().getConsole().sendMessage("§7IP-Ban: §c§lJa §8/ "+IPManager.getReasonString(IPManager.getIPFromPlayer(UUID))); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7IP-Ban: §a§lNein"); + } + BungeeCord.getInstance().getConsole().sendMessage("§7Bans: §e§l"+IPManager.getBans(IP)); + BungeeCord.getInstance().getConsole().sendMessage("§7Zuletzt genutzt: §e§l"+BanManager.formatTimestamp(IPManager.getLastUseLong(IP))); + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cZu dieser IP-Adresse sind keine Informationen verfügbar"); + } + } else { + if(BanManager.playerExists(UUID)){ + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + BungeeCord.getInstance().getConsole().sendMessage("§7Spieler: §e§l"+BanManager.getNameByUUID(UUID)); + if(BanManager.isBanned(UUID)){ + BungeeCord.getInstance().getConsole().sendMessage("§7Gebannt: §c§lJa §8/ "+BanManager.getReasonString(UUID)); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7Gebannt: §a§lNein"); + } + if(BanManager.isMuted(UUID)){ + BungeeCord.getInstance().getConsole().sendMessage("§7Gemutet: §c§lJa §8/ "+BanManager.getReasonString(UUID)); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7Gemutet: §a§lNein"); + } + if(IPManager.isBanned(IPManager.getIPFromPlayer(UUID))){ + BungeeCord.getInstance().getConsole().sendMessage("§7IP-Ban: §c§lJa §8/ "+IPManager.getReasonString(IPManager.getIPFromPlayer(UUID))); + } else { + BungeeCord.getInstance().getConsole().sendMessage("§7IP-Ban: §a§lNein"); + } + BungeeCord.getInstance().getConsole().sendMessage("§7Bans: §e§l"+BanManager.getBans(UUID)); + BungeeCord.getInstance().getConsole().sendMessage("§7Mutes: §e§l"+BanManager.getMutes(UUID)); + if(BanManager.getLastLogin(UUID) != null){ + BungeeCord.getInstance().getConsole().sendMessage("§7Letzter Login: §e§l"+BanManager.formatTimestamp(Long.valueOf(BanManager.getLastLogin(UUID)))); + } + if(BanManager.getFirstLogin(UUID) != null){ + BungeeCord.getInstance().getConsole().sendMessage("§7Erster Login: §e§l"+BanManager.formatTimestamp(Long.valueOf(BanManager.getFirstLogin(UUID)))); + } + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } + } + } +} diff --git a/src/de/tutorialwork/commands/IPBan.java b/src/de/tutorialwork/commands/IPBan.java new file mode 100644 index 0000000..600f54c --- /dev/null +++ b/src/de/tutorialwork/commands/IPBan.java @@ -0,0 +1,111 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.IPManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.regex.Pattern; + +public class IPBan extends Command { + public IPBan(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.ipban") || p.hasPermission("professionalbans.*")){ + if(args.length == 0 || args.length == 1){ + for(int zaehler = 1; zaehler < BanManager.countReasons()+1; zaehler++) { + if(BanManager.isBanReason(zaehler)){ + p.sendMessage("§7"+zaehler+" §8| §e"+BanManager.getReasonByID(zaehler)); + } + } + p.sendMessage(Main.Prefix+"/ipban "); + } else { + String IP = args[0]; + int ID = Integer.valueOf(args[1]); + if(validate(IP)){ + if(BanManager.getReasonByID(ID) != null){ + if(IPManager.IPExists(IP)){ + IPManager.ban(IP, ID, p.getUniqueId().toString()); + IPManager.addBan(IP); + BanManager.sendNotify("IPBAN", IP, p.getName(), BanManager.getReasonByID(ID)); + } else { + IPManager.insertIP(IP, null); + IPManager.ban(IP, ID, p.getUniqueId().toString()); + IPManager.addBan(IP); + BanManager.sendNotify("IPBAN", IP, p.getName(), BanManager.getReasonByID(ID)); + } + disconnectIPBannedPlayers(IP); + LogManager.createEntry(null, p.getUniqueId().toString(), "IPBAN_IP", IP); + } else { + p.sendMessage(Main.Prefix+"§cDieser Grund existiert nicht"); + } + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(IPManager.getIPFromPlayer(UUID) != null){ + String DBIP = IPManager.getIPFromPlayer(UUID); + IPManager.ban(DBIP, ID, p.getUniqueId().toString()); + IPManager.addBan(DBIP); + BanManager.sendNotify("IPBAN", DBIP, p.getName(), BanManager.getReasonByID(ID)); + disconnectIPBannedPlayers(DBIP); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "IPBAN_PLAYER", String.valueOf(ID)); + } else { + p.sendMessage(Main.Prefix+"§cZu diesem Spieler ist keine IP-Adresse gespeichert"); + } + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } + } + + public static void disconnectIPBannedPlayers(String IP){ + for(ProxiedPlayer all : Main.getInstance().getProxy().getPlayers()){ + if(all.getAddress().getHostString().equals(IP)){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + + if(IPManager.getRAWEnd(IP) == -1L){ + all.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.IPBAN").replace("%grund%", IPManager.getReasonString(IP)))); + } else { + if(System.currentTimeMillis() < IPManager.getRAWEnd(IP)){ + String MSG = configcfg.getString("LAYOUT.TEMPIPBAN"); + MSG = MSG.replace("%grund%", IPManager.getReasonString(IP)); + MSG = MSG.replace("%dauer%", IPManager.getEnd(IP)); + all.disconnect(ChatColor.translateAlternateColorCodes('&', MSG)); + } else { + IPManager.unban(IP); + } + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e2) { + e2.printStackTrace(); + } + } + } + } + + private static final Pattern PATTERN = Pattern.compile( + "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); + + public static boolean validate(final String ip) { + return PATTERN.matcher(ip).matches(); + } +} diff --git a/src/de/tutorialwork/commands/Kick.java b/src/de/tutorialwork/commands/Kick.java new file mode 100644 index 0000000..111b20e --- /dev/null +++ b/src/de/tutorialwork/commands/Kick.java @@ -0,0 +1,80 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Kick extends Command { + public Kick(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.kick") || p.hasPermission("professionalbans.*")){ + if(args.length == 0 || args.length == 1){ + p.sendMessage(Main.Prefix+"/kick "); + } else { + ProxiedPlayer tokick = BungeeCord.getInstance().getPlayer(args[0]); + if(tokick != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + String grund = ""; + for(int i = 1; i < args.length; i++){ + grund = grund + " " + args[i]; + } + BanManager.sendNotify("KICK", tokick.getName(), p.getName(), grund); + LogManager.createEntry(tokick.getUniqueId().toString(), p.getUniqueId().toString(), "KICK", grund); + tokick.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.KICK").replace("%grund%", grund))); + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler ist nicht online"); + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + if(args.length == 0 || args.length == 1){ + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/kick "); + } else { + ProxiedPlayer tokick = BungeeCord.getInstance().getPlayer(args[0]); + if(tokick != null){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + String grund = ""; + for(int i = 1; i < args.length; i++){ + grund = grund + " " + args[i]; + } + BanManager.sendNotify("KICK", tokick.getName(), "KONSOLE", grund); + LogManager.createEntry(tokick.getUniqueId().toString(), "KONSOLE", "KICK", grund); + tokick.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.KICK").replace("%grund%", grund))); + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Spieler ist nicht online"); + } + } + } + } +} diff --git a/src/de/tutorialwork/commands/ProfessionalBans.java b/src/de/tutorialwork/commands/ProfessionalBans.java new file mode 100644 index 0000000..8a9f6c4 --- /dev/null +++ b/src/de/tutorialwork/commands/ProfessionalBans.java @@ -0,0 +1,24 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +public class ProfessionalBans extends Command { + public ProfessionalBans(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + ProxiedPlayer p = (ProxiedPlayer) sender; + p.sendMessage(""); + p.sendMessage("§8[]===================================[]"); + p.sendMessage("§e§lProfessionalBans §7§oReloaded §8• §7Version §8» §c"+ Main.Version); + p.sendMessage("§7Developer §8» §e§lTutorialwork"); + p.sendMessage("§5YT §7Kanal §8» §cyoutube.com/Tutorialwork"); + p.sendMessage("§8[]===================================[]"); + p.sendMessage(""); + } +} diff --git a/src/de/tutorialwork/commands/Report.java b/src/de/tutorialwork/commands/Report.java new file mode 100644 index 0000000..ffd3ab4 --- /dev/null +++ b/src/de/tutorialwork/commands/Report.java @@ -0,0 +1,84 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Report extends Command { + public Report(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer) { + ProxiedPlayer p = (ProxiedPlayer) sender; + if(args.length == 0 || args.length == 1){ + String reasons = ""; + int komma = Main.reportreasons.size(); + for(String reason : Main.reportreasons){ + komma--; + if(komma != 0){ + reasons = reasons + reason + ", "; + } else { + reasons = reasons + reason; + } + } + p.sendMessage(Main.Prefix+"Verfügbare Reportgründe: §e§l"+reasons); + p.sendMessage(Main.Prefix+"/report "); + } else { + if(args[0].toUpperCase().equals(p.getName().toUpperCase())){ + p.sendMessage(Main.Prefix+"§cDu kannst dich nicht selbst melden"); + return; + } + if(Main.reportreasons.contains(args[1].toUpperCase())){ + ProxiedPlayer target = BungeeCord.getInstance().getPlayer(args[0]); + if(target != null){ + BanManager.createReport(target.getUniqueId().toString(), p.getUniqueId().toString(), args[1].toUpperCase(), null); + p.sendMessage(Main.Prefix+"Der Spieler §e§l"+target.getName()+" §7wurde erfolgreich wegen §e§l"+args[1].toUpperCase()+" §7gemeldet"); + BanManager.sendNotify("REPORT", target.getName(), p.getName(), args[1].toUpperCase()); + LogManager.createEntry(target.getUniqueId().toString(), p.getUniqueId().toString(), "REPORT", args[1].toUpperCase()); + } else { + try{ + File file = new File(Main.main.getDataFolder(), "config.yml"); + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); + if(cfg.getBoolean("REPORTS.OFFLINEREPORTS")){ + String UUID = UUIDFetcher.getUUID(args[0]); + if(UUID != null){ + if(BanManager.playerExists(UUID)){ + BanManager.createReport(UUID, p.getUniqueId().toString(), args[1].toUpperCase(), null); + p.sendMessage(Main.Prefix+"Der Spieler §e§l"+target.getName()+" §7(§4Offline§7) wurde erfolgreich wegen §e§l"+args[1].toUpperCase()+" §7gemeldet"); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "REPORT_OFFLINE", args[1].toUpperCase()); + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler wurde nicht gefunden"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler wurde nicht gefunden"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler ist offline"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + p.sendMessage(Main.Prefix+"§cDer eingegebene Reportgrund wurde nicht gefunden"); + } + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§lReports §7sind nur als Spieler verfügbar"); + } + } +} diff --git a/src/de/tutorialwork/commands/Reports.java b/src/de/tutorialwork/commands/Reports.java new file mode 100644 index 0000000..d09e0c6 --- /dev/null +++ b/src/de/tutorialwork/commands/Reports.java @@ -0,0 +1,77 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.File; +import java.io.IOException; + +public class Reports extends Command { + public Reports(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer) { + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.reports") || p.hasPermission("professionalbans.*")){ + if(args.length == 0){ + if(BanManager.countOpenReports() != 0){ + p.sendMessage("§8[]===================================[]"); + p.sendMessage("§e§loffene Reports §7(§8"+BanManager.countOpenReports()+"§7)"); + int offline = 0; + for(int i = 0; i < BanManager.getIDsFromOpenReports().size(); ++i) { + int id = (int) BanManager.getIDsFromOpenReports().get(i); + ProxiedPlayer target = BungeeCord.getInstance().getPlayer(BanManager.getNameByReportID(id)); + if(target != null){ + TextComponent tc = new TextComponent(); + tc.setText("§e§l"+target.getName()+" §7gemeldet wegen §c§l "+BanManager.getReasonByReportID(id)+" §8| §7Online auf §e§l"+target.getServer().getInfo().getName()); + tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/reports jump "+target.getName()+" "+id)); + tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Klicken um §e§l"+target.getName()+" §7nachzuspringen").create())); + p.sendMessage(tc); + } else { + offline++; + } + } + if(offline != 0){ + p.sendMessage("§4§o"+offline+" Reports §7§ovon Spieler die offline sind ausgeblendet"); + } + p.sendMessage("§8[]===================================[]"); + } else { + p.sendMessage(Main.Prefix+"§cEs sind derzeit keine Reports offen"); + } + } else if(args[0].equalsIgnoreCase("jump")){ + ProxiedPlayer target = BungeeCord.getInstance().getPlayer(args[1]); + if(target != null){ + p.connect(target.getServer().getInfo()); + int id = Integer.parseInt(args[2]); + BanManager.setReportDone(id); + BanManager.setReportTeamUUID(id, p.getUniqueId().toString()); + p.sendMessage(Main.Prefix+"Du hast den Report von §e§l"+BanManager.getNameByReportID(id)+" §7wegen §c§l"+BanManager.getReasonByReportID(id)+" §aangenommen"); + LogManager.createEntry(p.getUniqueId().toString(), null, "REPORT_ACCEPT", String.valueOf(id)); + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler ist nicht mehr online"); + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§lReports §7sind nur als Spieler verfügbar"); + } + } +} diff --git a/src/de/tutorialwork/commands/SupportChat.java b/src/de/tutorialwork/commands/SupportChat.java new file mode 100644 index 0000000..e7d6df1 --- /dev/null +++ b/src/de/tutorialwork/commands/SupportChat.java @@ -0,0 +1,137 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +import java.util.HashMap; + +public class SupportChat extends Command { + public SupportChat(String name) { + super(name); + } + + public static HashMap openchats = new HashMap<>(); + public static HashMap activechats = new HashMap<>(); + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(args.length != 0){ + if(args[0].equalsIgnoreCase("end")){ + if(activechats.containsValue(p) || activechats.containsKey(p)){ + for(ProxiedPlayer key : SupportChat.activechats.keySet()){ + //Key has started the support chat + if(key == p){ + SupportChat.activechats.get(p).sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7hat den Support Chat §cbeeendet"); + activechats.remove(key); + } else { + key.sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7hat den Support Chat §cbeeendet"); + activechats.remove(key); + } + } + p.sendMessage(Main.Prefix+"§cDu hast den Support Chat beendet"); + return; + } else { + p.sendMessage(Main.Prefix+"§cDu hast derzeit keinen offenen Support Chat"); + return; + } + } + } + if(p.hasPermission("professionalbans.supportchat") || p.hasPermission("professionalbans.*")){ + //Team Member + if(args.length > 0){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.getName().equals(args[0])){ + if (openchats.containsKey(all)) { + activechats.put(all, p); + openchats.remove(all); + all.sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7ist jetzt mit dir im Support Chat"); + all.sendMessage(Main.Prefix+"§8§oDu kannst in den Support Chat schreiben in dem du einfach eine normale Nachricht schreibst"); + all.sendMessage(Main.Prefix+"§8§oDu kannst den Support Chat mit §7§o/support end §8§obeenden"); + p.sendMessage(Main.Prefix+"§e§l"+all.getName()+" §7ist jetzt im Support Chat mit dir"); + p.sendMessage(Main.Prefix+"§8§oDu kannst den Support Chat mit §7§o/support end §8§obeenden"); + } else { + p.sendMessage(Main.Prefix+"§cDiese Anfrage ist ausgelaufen"); + } + } + } + } else { + if(SupportChat.openchats.size() != 0){ + p.sendMessage("§8[]===================================[]"); + int i = 0; + for(ProxiedPlayer key : SupportChat.openchats.keySet()){ + p.sendMessage("§e§l"+key+" §8• §9"+SupportChat.openchats.get(key)); + TextComponent tc = new TextComponent(); + tc.setText("§aSupport Chat starten"); + tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/support "+key)); + tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Klicken um den Chat mit §e§l"+key+" §7zu starten").create())); + p.sendMessage(tc); + i++; + } + p.sendMessage("§8[]===================================[]"); + p.sendMessage(Main.Prefix+"Es sind derzeit §e§l"+i+" Support Chats §7Anfragen §aoffen"); + } else { + p.sendMessage(Main.Prefix+"§cDerzeit sind keine Support Chats Anfragen offen"); + } + } + } else { + //Normal Member + if(args.length == 0){ + p.sendMessage(Main.Prefix+"Wenn du den §e§lSupport Chat §7starten möchtest gebe ein §8§oBetreff §7ein"); + p.sendMessage(Main.Prefix+"Möchtest du eine Anfrage abbrechen? §8§o/support cancel"); + } else { + int supporter = 0; + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.supportchat") || all.hasPermission("professionalbans.*")){ + supporter++; + } + } + if(!args[0].equalsIgnoreCase("cancel")){ + String subject = ""; + for(int i = 0; i < args.length; i++){ + subject = subject + " " + args[i]; + } + if(!openchats.containsKey(p)){ + if(supporter > 0){ + openchats.put(p, subject); + p.sendMessage(Main.Prefix+"Du hast eine Anfrage mit dem Betreff §e§l"+subject+" §7gestartet"); + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.supportchat") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7benötigt Support §8(§e§o"+subject+"§8)"); + TextComponent tc = new TextComponent(); + tc.setText("§aSupport Chat starten"); + tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/support "+p.getName())); + tc.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§7Klicken um den Chat mit §e§l"+p.getName()+" §7zu starten").create())); + all.sendMessage(tc); + } + } + } else { + p.sendMessage(Main.Prefix+"§cDerzeit ist kein Supporter online"); + } + } else { + p.sendMessage(Main.Prefix+"Du hast bereits eine §e§lSupport Chat §7Anfrage gestellt"); + p.sendMessage(Main.Prefix+"Möchtest du diese Anfrage §cabbrechen §7benutze §c§l/support cancel"); + } + } else { + if(!openchats.containsKey(p)){ + openchats.remove(p); + p.sendMessage(Main.Prefix+"Deine Anfrage wurde erfolgreich §cgelöscht"); + } else { + p.sendMessage(Main.Prefix+"§cDu hast derzeit keine offene Anfrage"); + } + } + } + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"Der §e§lSupport Chat §7ist nur als Spieler verfügbar"); + } + } +} diff --git a/src/de/tutorialwork/commands/Unban.java b/src/de/tutorialwork/commands/Unban.java new file mode 100644 index 0000000..a6e8655 --- /dev/null +++ b/src/de/tutorialwork/commands/Unban.java @@ -0,0 +1,95 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.IPManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +public class Unban extends Command { + public Unban(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.unban") || p.hasPermission("professionalbans.*")){ + if(args.length == 0){ + p.sendMessage(Main.Prefix+"/unban "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(IPBan.validate(args[0])){ + IPManager.unban(args[0]); + BanManager.sendNotify("UNBANIP", args[0], p.getName(), null); + p.sendMessage(Main.Prefix+"§7Die IP-Adresse §e§l"+args[0]+" §7wurde §aerfolgreich §7entbannt"); + LogManager.createEntry(null, p.getUniqueId().toString(), "UNBAN_IP", args[0]); + } else { + if(BanManager.playerExists(UUID)){ + if(IPManager.isBanned(IPManager.getIPFromPlayer(UUID))){ + IPManager.unban(IPManager.getIPFromPlayer(UUID)); + p.sendMessage(Main.Prefix+"Die IP §e§l"+IPManager.getIPFromPlayer(UUID)+ " §7war gebannt und wurde ebenfalls §aentbannt"); + } + if(BanManager.isBanned(UUID)){ + BanManager.unban(UUID); + BanManager.sendNotify("UNBAN", BanManager.getNameByUUID(UUID), p.getName(), "null"); + p.sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerfolgreich §7entbannt"); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "UNBAN_BAN", null); + } else if(BanManager.isMuted(UUID)) { + BanManager.unmute(UUID); + BanManager.sendNotify("UNMUTE", BanManager.getNameByUUID(UUID), p.getName(), "null"); + p.sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerfolgreich §7entmutet"); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "UNBAN_MUTE", null); + } else { + p.sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7ist weder gebannt oder gemutet"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } else { + if(args.length == 0){ + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"/unban "); + } else { + String UUID = UUIDFetcher.getUUID(args[0]); + if(IPBan.validate(args[0])){ + IPManager.unban(args[0]); + BanManager.sendNotify("UNBANIP", args[0], "KONSOLE", null); + BungeeCord.getInstance().getConsole().sendMessage((Main.Prefix+"§7Die IP-Adresse §e§l"+args[0]+" §7wurde §aerfolgreich §7entbannt")); + LogManager.createEntry(null, "KONSOLE", "UNBAN_IP", args[0]); + } else { + if(BanManager.playerExists(UUID)){ + if(IPManager.isBanned(IPManager.getIPFromPlayer(UUID))){ + IPManager.unban(IPManager.getIPFromPlayer(UUID)); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"Die IP §e§l"+IPManager.getIPFromPlayer(UUID)+ " §7war gebannt und wurde ebenfalls §aentbannt"); + } + if(BanManager.isBanned(UUID)){ + BanManager.unban(UUID); + BanManager.sendNotify("UNBAN", BanManager.getNameByUUID(UUID), "KONSOLE", "null"); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerfolgreich §7entbannt"); + LogManager.createEntry(UUID, "KONSOLE", "UNBAN_BAN", null); + } else if(BanManager.isMuted(UUID)) { + BanManager.unmute(UUID); + BanManager.sendNotify("UNMUTE", BanManager.getNameByUUID(UUID), "KONSOLE", "null"); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerfolgreich §7entmutet"); + LogManager.createEntry(UUID, "KONSOLE", "UNBAN_MUTE", null); + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§e§l"+BanManager.getNameByUUID(UUID)+" §7ist weder gebannt oder gemutet"); + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } + } + } + } +} diff --git a/src/de/tutorialwork/commands/WebAccount.java b/src/de/tutorialwork/commands/WebAccount.java new file mode 100644 index 0000000..32bce8c --- /dev/null +++ b/src/de/tutorialwork/commands/WebAccount.java @@ -0,0 +1,93 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BCrypt; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +import java.util.Random; + +public class WebAccount extends Command { + public WebAccount(String name) { + super(name); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(p.hasPermission("professionalbans.webaccount") || p.hasPermission("professionalbans.*")){ + if(args.length == 0 || args.length == 1){ + p.sendMessage(Main.Prefix+"/webaccount [Rang]"); + } else { + if(args[0].equalsIgnoreCase("erstellen")){ + if(args.length == 2){ + p.sendMessage(Main.Prefix+"Du musst noch ein Rang des Accounts angeben §4Admin§7, §cMod§7, §9Sup"); + return; + } + String UUID = UUIDFetcher.getUUID(args[1]); + if(BanManager.playerExists(UUID)){ + if(!BanManager.webaccountExists(UUID)){ + ProxiedPlayer target = BungeeCord.getInstance().getPlayer(args[1]); + if(target != null){ + String rowPW = randomString(7); + String Hash = BCrypt.hashpw(rowPW, BCrypt.gensalt()); + if(args[2].equalsIgnoreCase("Admin")){ + BanManager.createWebAccount(UUID, BanManager.getNameByUUID(UUID), 3, Hash); + p.sendMessage(Main.Prefix+"Ein §4§lAdmin §7Account für §e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerstellt"); + } else if(args[2].equalsIgnoreCase("Mod")){ + BanManager.createWebAccount(UUID, BanManager.getNameByUUID(UUID), 2, Hash); + p.sendMessage(Main.Prefix+"Ein §c§lMod §7Account für §e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerstellt"); + } else if(args[2].equalsIgnoreCase("Sup")){ + BanManager.createWebAccount(UUID, BanManager.getNameByUUID(UUID), 1, Hash); + p.sendMessage(Main.Prefix+"Ein §9§lSup §7Account für §e§l"+BanManager.getNameByUUID(UUID)+" §7wurde §aerstellt"); + } + target.sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7hat einen Webaccount für dich erstellt"); + target.sendMessage(Main.Prefix+"Passwort: §c§l"+rowPW); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "ADD_WEBACCOUNT", args[2]); + } else { + p.sendMessage(Main.Prefix+"§e§l"+args[1]+" §7ist derzeit nicht online"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat bereits einen Zugang zum Webinterface"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } else if(args[0].equalsIgnoreCase("löschen")){ + String UUID = UUIDFetcher.getUUID(args[1]); + if(BanManager.playerExists(UUID)){ + if(BanManager.webaccountExists(UUID)){ + BanManager.deleteWebAccount(UUID); + p.sendMessage(Main.Prefix+"Der Zugang von dem Spieler §e§l"+BanManager.getNameByUUID(UUID)+" §7wurde erfolgreich §agelöscht"); + LogManager.createEntry(UUID, p.getUniqueId().toString(), "DEL_WEBACCOUNT", null); + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat keinen Zugang zum Webinterface"); + } + } else { + p.sendMessage(Main.Prefix+"§cDieser Spieler hat den Server noch nie betreten"); + } + } else { + p.sendMessage(Main.Prefix+"§cDiese Aktion ist nicht gültig"); + } + } + } else { + p.sendMessage(Main.NoPerms); + } + } + } + static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; + static Random rnd = new Random(); + + private static String randomString(int length){ + StringBuilder sb = new StringBuilder(length); + for(int i = 0; i < length; i++) + sb.append(AB.charAt(rnd.nextInt(AB.length()))); + return sb.toString(); + } +} diff --git a/src/de/tutorialwork/commands/WebVerify.java b/src/de/tutorialwork/commands/WebVerify.java new file mode 100644 index 0000000..283c3eb --- /dev/null +++ b/src/de/tutorialwork/commands/WebVerify.java @@ -0,0 +1,46 @@ +package de.tutorialwork.commands; + +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Command; + +public class WebVerify extends Command { + public WebVerify(String cmd) { + super(cmd); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if(sender instanceof ProxiedPlayer){ + ProxiedPlayer p = (ProxiedPlayer) sender; + if(args.length == 0){ + p.sendMessage(Main.Prefix+"/webverify "); + } else { + String UUID = p.getUniqueId().toString(); + if(BanManager.webaccountExists(UUID)){ + if(BanManager.hasAuthToken(UUID)){ + if(args[0].length() == 25){ + if(BanManager.getAuthCode(UUID).equals(args[0])){ + BanManager.updateAuthStatus(UUID); + p.sendMessage(Main.Prefix+"§a§lErfolgreich! §7Du kannst jetzt dein Passwort festlegen."); + } else { + p.sendMessage(Main.Prefix+"§cDer eingegebene Token ist ungültig"); + } + } else { + p.sendMessage(Main.Prefix+"§cDer eingegebene Token ist ungültig"); + } + } else { + p.sendMessage(Main.Prefix+"§cEs wurde keine Verifizierungsanfrage von dir gefunden"); + } + } else { + p.sendMessage(Main.Prefix+"§cDu hast keinen Account im Webinterface"); + } + } + } else { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDieser Befehl ist nur als Spieler nutzbar"); + } + } +} diff --git a/src/de/tutorialwork/listener/Chat.java b/src/de/tutorialwork/listener/Chat.java new file mode 100644 index 0000000..f2f892b --- /dev/null +++ b/src/de/tutorialwork/listener/Chat.java @@ -0,0 +1,204 @@ +package de.tutorialwork.listener; + +import de.tutorialwork.commands.SupportChat; +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.LogManager; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ChatEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; +import net.md_5.bungee.event.EventHandler; + +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; + +public class Chat implements Listener { + + @EventHandler + public void onChat(ChatEvent e){ + ProxiedPlayer p = (ProxiedPlayer) e.getSender(); + if(!e.getMessage().startsWith("/")){ + if(SupportChat.activechats.containsKey(p)){ + e.setCancelled(true); + ProxiedPlayer target = SupportChat.activechats.get(p); + target.sendMessage("§9§lSUPPORT §8• §c"+p.getName()+" §8» "+e.getMessage()); + p.sendMessage("§9§lSUPPORT §8• §aDu §8» "+e.getMessage()); + } + if(SupportChat.activechats.containsValue(p)){ + e.setCancelled(true); + for(ProxiedPlayer key : SupportChat.activechats.keySet()){ + //Key has started the support chat + key.sendMessage("§9§lSUPPORT §8• §c"+p.getName()+" §8» "+e.getMessage()); + } + p.sendMessage("§9§lSUPPORT §8• §aDu §8» "+e.getMessage()); + } + if(BanManager.isMuted(p.getUniqueId().toString())){ + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + + if(BanManager.getRAWEnd(p.getUniqueId().toString()) == -1L){ + e.setCancelled(true); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.MUTE").replace("%grund%", BanManager.getReasonString(p.getUniqueId().toString())))); + } else { + if(System.currentTimeMillis() < BanManager.getRAWEnd(p.getUniqueId().toString())){ + e.setCancelled(true); + String MSG = configcfg.getString("LAYOUT.TEMPMUTE"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(p.getUniqueId().toString())); + MSG = MSG.replace("%dauer%", BanManager.getEnd(p.getUniqueId().toString())); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', MSG)); + } else { + BanManager.unmute(p.getUniqueId().toString()); + } + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e2) { + e2.printStackTrace(); + } + } else { + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + + if(!p.hasPermission("professionalbans.blacklist.bypass") || !p.hasPermission("professionalbans.*")){ + if(configcfg.getBoolean("AUTOMUTE.ENABLED")){ + insertMessage(p.getUniqueId().toString(), e.getMessage(), p.getServer().getInfo().getName()); + for(String blacklist : Main.blacklist){ + if(e.getMessage().toUpperCase().contains(blacklist.toUpperCase())){ + e.setCancelled(true); + BanManager.mute(p.getUniqueId().toString(), configcfg.getInt("AUTOMUTE.MUTEID"), "KONSOLE"); + LogManager.createEntry(p.getUniqueId().toString(), "KONSOLE", "AUTOMUTE_BLACKLIST", e.getMessage()); + BanManager.setMutes(p.getUniqueId().toString(), BanManager.getMutes(p.getUniqueId().toString()) + 1); + BanManager.sendNotify("MUTE", BanManager.getNameByUUID(p.getUniqueId().toString()), "KONSOLE", BanManager.getReasonByID(configcfg.getInt("AUTOMUTE.MUTEID"))); + if(BanManager.getRAWEnd(p.getUniqueId().toString()) == -1L){ + p.sendMessage(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.MUTE").replace("%grund%", BanManager.getReasonByID(configcfg.getInt("AUTOMUTE.MUTEID"))))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPMUTE"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(p.getUniqueId().toString())); + MSG = MSG.replace("%dauer%", BanManager.getEnd(p.getUniqueId().toString())); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', MSG)); + } + return; + } + } + for(String adblacklist : Main.adblacklist){ + if(e.getMessage().toUpperCase().contains(adblacklist.toUpperCase())){ + if(!Main.adwhitelist.contains(e.getMessage().toUpperCase())){ + e.setCancelled(true); + BanManager.mute(p.getUniqueId().toString(), configcfg.getInt("AUTOMUTE.ADMUTEID"), "KONSOLE"); + LogManager.createEntry(p.getUniqueId().toString(), "KONSOLE", "AUTOMUTE_ADBLACKLIST", e.getMessage()); + BanManager.setMutes(p.getUniqueId().toString(), BanManager.getMutes(p.getUniqueId().toString()) + 1); + BanManager.sendNotify("MUTE", BanManager.getNameByUUID(p.getUniqueId().toString()), "KONSOLE", BanManager.getReasonByID(configcfg.getInt("AUTOMUTE.ADMUTEID"))); + if(BanManager.getRAWEnd(p.getUniqueId().toString()) == -1L){ + p.sendMessage(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.MUTE").replace("%grund%", BanManager.getReasonByID(configcfg.getInt("AUTOMUTE.MUTEID"))))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPMUTE"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(p.getUniqueId().toString())); + MSG = MSG.replace("%dauer%", BanManager.getEnd(p.getUniqueId().toString())); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', MSG)); + } + return; + } + } + } + } else { + insertMessage(p.getUniqueId().toString(), e.getMessage(), p.getServer().getInfo().getName()); + if(configcfg.getBoolean("AUTOMUTE.AUTOREPORT")){ + for(String blacklist : Main.blacklist){ + if(e.getMessage().toUpperCase().contains(blacklist.toUpperCase())){ + e.setCancelled(true); + p.sendMessage(Main.Prefix+"§cAchte auf deine Wortwahl"); + String LogID = Chat.createChatlog(p.getUniqueId().toString(), "KONSOLE"); + BanManager.createReport(p.getUniqueId().toString(),"KONSOLE", "VERHALTEN", LogID); + BanManager.sendNotify("REPORT", p.getName(), "KONSOLE", "VERHALTEN"); + return; + } + } + for(String adblacklist : Main.adblacklist){ + if(e.getMessage().toUpperCase().contains(adblacklist.toUpperCase())){ + if(!Main.adwhitelist.contains(e.getMessage().toUpperCase())){ + e.setCancelled(true); + p.sendMessage(Main.Prefix+"§cDu darfst keine Werbung machen"); + String LogID = Chat.createChatlog(p.getUniqueId().toString(), "KONSOLE"); + BanManager.createReport(p.getUniqueId().toString(),"KONSOLE", "WERBUNG", LogID); + BanManager.sendNotify("REPORT", p.getName(), "KONSOLE", "WERBUNG"); + return; + } + } + } + } + } + } else { + insertMessage(p.getUniqueId().toString(), e.getMessage(), p.getServer().getInfo().getName()); + } + + } catch (IOException e2) { + e2.printStackTrace(); + } + } + } + } + + public static void insertMessage(String UUID, String Message, String Server){ + Main.mysql.update("INSERT INTO chat(UUID, SERVER, MESSAGE, SENDDATE) " + + "VALUES ('" + UUID + "', '" + Server + "', '" + Message + "', '" + System.currentTimeMillis() + "')"); + } + + public static String createChatlog(String UUID, String CreatedUUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM chat WHERE UUID='" + UUID + "'"); + String ID = randomString(20); + Long now = System.currentTimeMillis(); + while (rs.next()){ + int TEN_MINUTES = 10 * 60 * 1000; + long tenAgo = System.currentTimeMillis() - TEN_MINUTES; + if (Long.valueOf(rs.getString("SENDDATE")) > tenAgo) { + Main.mysql.update("INSERT INTO chatlog(LOGID, UUID, CREATOR_UUID, SERVER, MESSAGE, SENDDATE, CREATED_AT) " + + "VALUES ('" + ID + "' ,'" + UUID + "', '" + CreatedUUID + "', '" + rs.getString("SERVER") + "', '" + rs.getString("MESSAGE") + "', '" + rs.getString("SENDDATE") + "', '" + now + "')"); + } + } + return ID; + } catch (SQLException exc){ + + } + return null; + } + + public static boolean hasMessages(String UUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM chat WHERE UUID='" + UUID + "'"); + int i = 0; + while (rs.next()){ + i++; + } + if(i != 0){ + return true; + } else { + return false; + } + } catch (SQLException exc){ + + } + return false; + } + + static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + static SecureRandom rnd = new SecureRandom(); + + public static String randomString(int len){ + StringBuilder sb = new StringBuilder( len ); + for( int i = 0; i < len; i++ ) + sb.append( AB.charAt( rnd.nextInt(AB.length()) ) ); + return sb.toString(); + } + +} diff --git a/src/de/tutorialwork/listener/Login.java b/src/de/tutorialwork/listener/Login.java new file mode 100644 index 0000000..0133aa8 --- /dev/null +++ b/src/de/tutorialwork/listener/Login.java @@ -0,0 +1,135 @@ +package de.tutorialwork.listener; + +import de.tutorialwork.commands.SupportChat; +import de.tutorialwork.main.Main; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.IPManager; +import de.tutorialwork.utils.UUIDFetcher; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.PreLoginEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; +import net.md_5.bungee.event.EventHandler; + +import java.io.File; +import java.io.IOException; + +public class Login implements Listener { + + @EventHandler + public void onJoin(PreLoginEvent e){ + String UUID = UUIDFetcher.getUUID(e.getConnection().getName()); + String IP = e.getConnection().getAddress().getHostString(); + BanManager.createPlayer(UUID, e.getConnection().getName()); + IPManager.insertIP(IP, UUID); + File config = new File(Main.main.getDataFolder(), "config.yml"); + try{ + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(cfg.getBoolean("VPN.BLOCKED")){ + if(!Main.ipwhitelist.contains(IP)){ + if(IPManager.isVPN(IP)){ + if(cfg.getBoolean("VPN.KICK")){ + e.setCancelled(true); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', cfg.getString("VPN.KICKMSG"))); + } + if(cfg.getBoolean("VPN.BAN")){ + int id = cfg.getInt("VPN.BANID"); + BanManager.ban(UUID, id, "KONSOLE", Main.increaseValue, Main.increaseBans); + BanManager.sendNotify("IPBAN", e.getConnection().getAddress().getHostString(), "KONSOLE", BanManager.getReasonByID(id)); + e.setCancelled(true); + if(BanManager.getRAWEnd(UUID) == -1L){ + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', cfg.getString("LAYOUT.IPBAN").replace("%grund%", BanManager.getReasonByID(id)))); + } else { + String MSG = cfg.getString("LAYOUT.TEMPIPBAN"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', MSG)); + } + } + } + } + } + } catch (IOException er){ + er.printStackTrace(); + } + if(IPManager.isBanned(IP)){ + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + + if(IPManager.getRAWEnd(IP) == -1L){ + e.setCancelled(true); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.IPBAN").replace("%grund%", IPManager.getReasonString(IP)))); + } else { + if(System.currentTimeMillis() < IPManager.getRAWEnd(IP)){ + e.setCancelled(true); + String MSG = configcfg.getString("LAYOUT.TEMPIPBAN"); + MSG = MSG.replace("%grund%", IPManager.getReasonString(IP)); + MSG = MSG.replace("%dauer%", IPManager.getEnd(IP)); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', MSG)); + } else { + IPManager.unban(IP); + } + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e2) { + e2.printStackTrace(); + } + } + if(BanManager.isBanned(UUID)){ + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + + if(BanManager.getRAWEnd(UUID) == -1L){ + e.setCancelled(true); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.BAN").replace("%grund%", BanManager.getReasonString(UUID)))); + } else { + if(System.currentTimeMillis() < BanManager.getRAWEnd(UUID)){ + e.setCancelled(true); + String MSG = configcfg.getString("LAYOUT.TEMPBAN"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(UUID)); + MSG = MSG.replace("%dauer%", BanManager.getEnd(UUID)); + e.setCancelReason(ChatColor.translateAlternateColorCodes('&', MSG)); + } else { + BanManager.unban(UUID); + } + } + + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e2) { + e2.printStackTrace(); + } + } + } + + @EventHandler + public void onFinalLogin(PostLoginEvent e){ + ProxiedPlayer p = e.getPlayer(); + if(p.hasPermission("professionalbans.reports") || p.hasPermission("professionalbans.*")){ + if(BanManager.countOpenReports() != 0){ + p.sendMessage(Main.Prefix+"Derzeit sind noch §e§l"+BanManager.countOpenReports()+" Reports §7offen"); + } + } + if(p.hasPermission("professionalbans.supportchat") || p.hasPermission("professionalbans.*")){ + if(SupportChat.openchats.size() != 0){ + p.sendMessage(Main.Prefix+"Derzeit sind noch §e§l"+SupportChat.openchats.size()+" §7Support Chat Anfragen §aoffen"); + } + } + //Update Check + if(p.hasPermission("professionalbans.*")){ + if(!Main.callURL("https://api.spigotmc.org/legacy/update.php?resource=63657").equals(Main.Version)){ + p.sendMessage("§8[]===================================[]"); + p.sendMessage("§e§lProfessionalBans §7Reloaded §8| §7Version §c"+Main.Version); + p.sendMessage("§cDu benutzt eine §c§lVERALTETE §cVersion des Plugins!"); + p.sendMessage("§7Update: §4§lhttps://spigotmc.org/resources/63657"); + p.sendMessage("§8[]===================================[]"); + } + } + } + +} diff --git a/src/de/tutorialwork/listener/Quit.java b/src/de/tutorialwork/listener/Quit.java new file mode 100644 index 0000000..ffaa01b --- /dev/null +++ b/src/de/tutorialwork/listener/Quit.java @@ -0,0 +1,29 @@ +package de.tutorialwork.listener; + +import de.tutorialwork.commands.SupportChat; +import de.tutorialwork.main.Main; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +public class Quit implements Listener { + + @EventHandler + public static void onQuit(PlayerDisconnectEvent e){ + ProxiedPlayer p = e.getPlayer(); + if(SupportChat.activechats.containsKey(p) || SupportChat.activechats.containsValue(p)){ + for(ProxiedPlayer key : SupportChat.activechats.keySet()){ + //Key has started the support chat + if(key == p){ + SupportChat.activechats.get(p).sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7hat den Support hat §cbeeendet"); + SupportChat.activechats.remove(p); + } else { + key.sendMessage(Main.Prefix+"§e§l"+p.getName()+" §7hat den Support Chat §cbeeendet"); + SupportChat.activechats.remove(key); + } + } + } + } + +} diff --git a/src/de/tutorialwork/main/Main.java b/src/de/tutorialwork/main/Main.java new file mode 100644 index 0000000..ad1c57d --- /dev/null +++ b/src/de/tutorialwork/main/Main.java @@ -0,0 +1,481 @@ +package de.tutorialwork.main; + +import de.tutorialwork.commands.Blacklist; +import de.tutorialwork.commands.*; +import de.tutorialwork.listener.Chat; +import de.tutorialwork.listener.Login; +import de.tutorialwork.listener.Quit; +import de.tutorialwork.utils.BanManager; +import de.tutorialwork.utils.Metrics; +import de.tutorialwork.utils.MySQLConnect; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +public class Main extends Plugin { + + public static Main main; + public static MySQLConnect mysql; + public static String Prefix = "§e§lBANS §8• §7"; + public static String NoPerms = Prefix + "§cDu hast keine Berechtigung diesen Befehl zu nutzen"; + + public static ArrayList reportreasons = new ArrayList<>(); + public static ArrayList blacklist = new ArrayList<>(); + public static ArrayList adblacklist = new ArrayList<>(); + public static ArrayList adwhitelist = new ArrayList<>(); + public static ArrayList ipwhitelist = new ArrayList<>(); + + public static boolean increaseBans = true; + public static Integer increaseValue = 50; + + public static String APIKey = null; + + //============================================== + //Plugin Informationen + public static String Version = "2.3"; + //============================================== + + @Override + public void onEnable() { + main = this; + Config(); + MySQL(); + Commands(); + Listener(); + //============================================== + //Konsolen Nachricht über das Plugin + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + BungeeCord.getInstance().getConsole().sendMessage("§e§lProfessionalBans §7§oReloaded §8| §7Version: §c"+Version); + BungeeCord.getInstance().getConsole().sendMessage("§7Developer: §e§lTutorialwork"); + BungeeCord.getInstance().getConsole().sendMessage("§5YT §7Kanal: §cyoutube.com/Tutorialwork"); + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + //============================================== + //============================================== + //Überprüft auf Bans aus dem Webinterface + getProxy().getScheduler().schedule(this, new Runnable() { + @Override + public void run() { + File config = new File(Main.main.getDataFolder(), "config.yml"); + try { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + for(ProxiedPlayer all : getProxy().getPlayers()){ + if(BanManager.isBanned(all.getUniqueId().toString())){ + if(BanManager.getRAWEnd(all.getUniqueId().toString()) == -1L){ + all.disconnect(ChatColor.translateAlternateColorCodes('&', configcfg.getString("LAYOUT.BAN").replace("%grund%", BanManager.getReasonString(all.getUniqueId().toString())))); + } else { + String MSG = configcfg.getString("LAYOUT.TEMPBAN"); + MSG = MSG.replace("%grund%", BanManager.getReasonString(all.getUniqueId().toString())); + MSG = MSG.replace("%dauer%", BanManager.getEnd(all.getUniqueId().toString())); + all.disconnect(ChatColor.translateAlternateColorCodes('&', MSG)); + } + } + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } catch (IOException e) { + e.printStackTrace(); + } + } + }, 5, 5, TimeUnit.SECONDS); + //============================================== + //============================================== + //Updater + if(!callURL("https://api.spigotmc.org/legacy/update.php?resource=63657").equals(Version)){ + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + BungeeCord.getInstance().getConsole().sendMessage("§e§lProfessionalBans §7Reloaded §8| §7Version §c"+Version); + BungeeCord.getInstance().getConsole().sendMessage("§cDu benutzt eine §c§lVERALTETE §cVersion des Plugins!"); + BungeeCord.getInstance().getConsole().sendMessage("§7Update: §4§lhttps://spigotmc.org/resources/63657"); + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + } + //============================================== + Metrics metrics = new Metrics(this); + } + + private void Config() { + if(!getDataFolder().exists()){ + getDataFolder().mkdir(); + } + File file = new File(getDataFolder().getPath(), "mysql.yml"); + File config = new File(getDataFolder().getPath(), "config.yml"); + File blacklistfile = new File(getDataFolder().getPath(), "blacklist.yml"); + try { + if(!file.exists()){ + file.createNewFile(); + Configuration mysql = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); + mysql.set("HOST", "localhost"); + mysql.set("DATENBANK", "Bans"); + mysql.set("USER", "root"); + mysql.set("PASSWORT", "deinpasswort"); + ConfigurationProvider.getProvider(YamlConfiguration.class).save(mysql, file); + } + if(!config.exists()){ + config.createNewFile(); + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + configcfg.set("PREFIX", "&e&lBANS &8• &7"); + configcfg.set("LAYOUT.BAN", "&8[]===================================[] \n\n &4&lDu wurdest GEBANNT \n\n &eGrund: §c§l%grund% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.KICK", "&8[]===================================[] \n\n &e&lDu wurdest GEKICKT \n\n &eGrund: §c§l%grund% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.TEMPBAN", "&8[]===================================[] \n\n &4&lDu wurdest temporär GEBANNT \n\n &eGrund: §c§l%grund% \n &eRestzeit: &c&l%dauer% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.MUTE", "&8[]===================================[] \n\n &4&lDu wurdest GEMUTET \n\n &eGrund: §c§l%grund% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.TEMPMUTE", "&8[]===================================[] \n\n &4&lDu wurdest temporär GEMUTET \n\n &eGrund: §c§l%grund% \n &eRestzeit: &c&l%dauer% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.IPBAN", "&8[]===================================[] \n\n &4&lDeine IP-Adresse wurde GEBANNT \n\n &eGrund: §c§l%grund% \n\n&8[]===================================[]"); + configcfg.set("LAYOUT.TEMPIPBAN", "&8[]===================================[] \n\n &4&lDeine IP-Adresse wurde temporär GEBANNT \n\n &eGrund: §c§l%grund% \n &eRestzeit: &c&l%dauer% \n\n&8[]===================================[]"); + configcfg.set("VPN.BLOCKED", true); + configcfg.set("VPN.KICK", true); + configcfg.set("VPN.KICKMSG", "&7Das benutzen einer &4VPN &7ist auf unserem Netzwerk &cUNTERSAGT"); + configcfg.set("VPN.BAN", false); + configcfg.set("VPN.BANID", 0); + ipwhitelist.add("8.8.8.8"); + configcfg.set("VPN.WHITELIST", ipwhitelist); + configcfg.set("VPN.APIKEY", "Go to https://proxycheck.io/dashboard and register with your email and enter here your API Key"); + configcfg.set("REPORTS.ENABLED", true); + reportreasons.add("Hacking"); + reportreasons.add("Verhalten"); + reportreasons.add("Teaming"); + reportreasons.add("TPA-Falle"); + reportreasons.add("Werbung"); + configcfg.set("REPORTS.REASONS", reportreasons); + configcfg.set("REPORTS.OFFLINEREPORTS", false); + configcfg.set("CHATLOG.ENABLED", true); + configcfg.set("CHATLOG.URL", "DeinServer.net/BanWebinterface/public/chatlog.php?id="); + configcfg.set("AUTOMUTE.ENABLED", false); + configcfg.set("AUTOMUTE.AUTOREPORT", true); + //configcfg.set("AUTOMUTE.AUTOREPORT.REASON", "Automatischer Report"); + configcfg.set("AUTOMUTE.MUTEID", 0); + configcfg.set("AUTOMUTE.ADMUTEID", 0); + configcfg.set("BANTIME-INCREASE.ENABLED", true); + configcfg.set("BANTIME-INCREASE.PERCENTRATE", 50); + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } else { + Configuration configcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(config); + if(configcfg.getBoolean("VPN.KICK") && configcfg.getBoolean("VPN.BAN")){ + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + BungeeCord.getInstance().getConsole().sendMessage("§c§lSINNLOSE EINSTELLUNG ENTDECKT"); + BungeeCord.getInstance().getConsole().sendMessage("§7Wenn ein Spieler mit einer VPN das Netzwerk betritt kann er nicht gekickt UND gebannt werden."); + BungeeCord.getInstance().getConsole().sendMessage("§4§lÜberprüfe die VPN Einstellung in der CONFIG.YML"); + BungeeCord.getInstance().getConsole().sendMessage("§8[]===================================[]"); + //Setze VPN Einstellung zurück! + configcfg.set("VPN.BLOCKED", true); + configcfg.set("VPN.KICK", true); + configcfg.set("VPN.KICKMSG", "&7Das benutzen einer &4VPN &7ist auf unserem Netzwerk &cUNTERSAGT"); + configcfg.set("VPN.BAN", false); + configcfg.set("VPN.BANID", 0); + } + for(String reasons : configcfg.getStringList("REPORTS.REASONS")){ + reportreasons.add(reasons.toUpperCase()); + } + for(String ips : configcfg.getStringList("VPN.WHITELIST")){ + ipwhitelist.add(ips); + } + Prefix = configcfg.getString("PREFIX").replace("&", "§"); + increaseBans = configcfg.getBoolean("BANTIME-INCREASE.ENABLED"); + increaseValue = configcfg.getInt("BANTIME-INCREASE.PERCENTRATE"); + if(configcfg.getString("VPN.APIKEY").length() == 27){ + APIKey = configcfg.getString("VPN.APIKEY"); + } + ConfigurationProvider.getProvider(YamlConfiguration.class).save(configcfg, config); + } + if(!blacklistfile.exists()){ + blacklistfile.createNewFile(); + Configuration blacklistcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(blacklistfile); + adwhitelist.add("DeinServer.net"); + adwhitelist.add("forum.DeinServer.net"); + adwhitelist.add("ts.DeinServer.net"); + blacklistcfg.set("ADWHITELIST", adwhitelist); + adblacklist.add(".com"); + adblacklist.add(".org"); + adblacklist.add(".net"); + adblacklist.add(".us"); + adblacklist.add(".co"); + adblacklist.add(".de"); + adblacklist.add(".biz"); + adblacklist.add(".info"); + adblacklist.add(".name"); + adblacklist.add(".yt"); + adblacklist.add(".tv"); + adblacklist.add(".xyz"); + adblacklist.add(".fr"); + adblacklist.add(".ch"); + adblacklist.add(".au"); + adblacklist.add(".at"); + adblacklist.add(".in"); + adblacklist.add(".jp"); + adblacklist.add(".nl"); + adblacklist.add(".uk"); + adblacklist.add(".no"); + adblacklist.add(".ru"); + adblacklist.add(".br"); + adblacklist.add(".tk"); + adblacklist.add(".ml"); + adblacklist.add(".ga"); + adblacklist.add(".cf"); + adblacklist.add(".gq"); + adblacklist.add(".ip"); + adblacklist.add(".dee"); + adblacklist.add(".d e"); + adblacklist.add("[punkt]"); + adblacklist.add("(punkt)"); + adblacklist.add("join now"); + adblacklist.add("join"); + adblacklist.add("mein server"); + adblacklist.add("mein netzwerk"); + adblacklist.add("www"); + adblacklist.add("[.]"); + adblacklist.add("(,)"); + adblacklist.add("(.)"); + blacklistcfg.set("ADBLACKLIST", adblacklist); + blacklist.add("anal"); + blacklist.add("anus"); + blacklist.add("b1tch"); + blacklist.add("bang"); + blacklist.add("banger"); + blacklist.add("bastard"); + blacklist.add("biatch"); + blacklist.add("bitch"); + blacklist.add("bitches"); + blacklist.add("blow job"); + blacklist.add("blow"); + blacklist.add("blowjob"); + blacklist.add("boob"); + blacklist.add("boobs"); + blacklist.add("bullshit"); + blacklist.add("bull shit"); + blacklist.add("c0ck"); + blacklist.add("cock"); + blacklist.add("d1ck"); + blacklist.add("d1ld0"); + blacklist.add("d1ldo"); + blacklist.add("dick"); + blacklist.add("doggie-style"); + blacklist.add("doggy-style"); + blacklist.add("f.u.c.k"); + blacklist.add("fack"); + blacklist.add("faggit"); + blacklist.add("faggot"); + blacklist.add("fagot"); + blacklist.add("fuck"); + blacklist.add("f-u-c-k"); + blacklist.add("ficken"); + blacklist.add("fick"); + blacklist.add("fuckoff"); + blacklist.add("fucks"); + blacklist.add("fuk"); + blacklist.add("fvck"); + blacklist.add("fxck"); + blacklist.add("gai"); + blacklist.add("gay"); + blacklist.add("schwul"); + blacklist.add("schwuchtel"); + blacklist.add("h0m0"); + blacklist.add("h0mo"); + blacklist.add("hitler"); + blacklist.add("homo"); + blacklist.add("lesbe"); + blacklist.add("nigga"); + blacklist.add("niggah"); + blacklist.add("nigger"); + blacklist.add("nippel"); + blacklist.add("pedo"); + blacklist.add("pedo"); + blacklist.add("penis"); + blacklist.add("porn"); + blacklist.add("porno"); + blacklist.add("pornografie"); + blacklist.add("sex"); + blacklist.add("sh1t"); + blacklist.add("s-h-1-t"); + blacklist.add("shit"); + blacklist.add("s-h-i-t"); + blacklist.add("scheiße"); + blacklist.add("scheisse"); + blacklist.add("xxx"); + blacklist.add("Fotze"); + blacklist.add("Hackfresse"); + blacklist.add("Hurensohn"); + blacklist.add("Huso"); + blacklist.add("Hure"); + blacklist.add("hirnamputiert"); + blacklist.add("Honk"); + blacklist.add("kek"); + blacklist.add("Loser"); + blacklist.add("Mongo"); + blacklist.add("Pimmel"); + blacklist.add("Pimmelfresse"); + blacklist.add("Schlampe"); + blacklist.add("Spastard"); + blacklist.add("abspritzer"); + blacklist.add("afterlecker"); + blacklist.add("arschficker"); + blacklist.add("arschgeburt"); + blacklist.add("arschgeige"); + blacklist.add("arschgesicht"); + blacklist.add("arschlecker"); + blacklist.add("arschloch"); + blacklist.add("arschlöcher"); + blacklist.add("assi"); + blacklist.add("beklopter"); + blacklist.add("bummsen"); + blacklist.add("bumsen"); + blacklist.add("drecksack"); + blacklist.add("drecksau"); + blacklist.add("drecksfotze"); + blacklist.add("drecksnigger"); + blacklist.add("drecksnutte"); + blacklist.add("dreckspack"); + blacklist.add("dreckvotze"); + blacklist.add("fagette"); + blacklist.add("fagitt"); + blacklist.add("ficker"); + blacklist.add("fickfehler"); + blacklist.add("fickfresse"); + blacklist.add("fickgesicht"); + blacklist.add("ficknudel"); + blacklist.add("ficksau"); + blacklist.add("hackfresse"); + blacklist.add("lusche"); + blacklist.add("heil"); + blacklist.add("missgeburt"); + blacklist.add("mißgeburt"); + blacklist.add("miststück"); + blacklist.add("nazi"); + blacklist.add("nazis"); + blacklist.add("penner"); + blacklist.add("scheisser"); + blacklist.add("sieg heil"); + blacklist.add("vollidiot"); + blacklist.add("volldepp"); + blacklist.add("wanker"); + blacklist.add("wichser"); + blacklist.add("wichsvorlage"); + blacklist.add("wixa"); + blacklist.add("wixen"); + blacklist.add("wixer"); + blacklist.add("wixxer"); + blacklist.add("wixxxer"); + blacklist.add("wixxxxer"); + blacklistcfg.set("BLACKLIST", blacklist); + ConfigurationProvider.getProvider(YamlConfiguration.class).save(blacklistcfg, blacklistfile); + } else { + Configuration blacklistcfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(blacklistfile); + for(String congigstr : blacklistcfg.getStringList("BLACKLIST")){ + blacklist.add(congigstr); + } + for(String congigstr : blacklistcfg.getStringList("ADBLACKLIST")){ + adblacklist.add(congigstr); + } + for(String congigstr : blacklistcfg.getStringList("ADWHITELIST")){ + adwhitelist.add(congigstr.toUpperCase()); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void MySQL() { + try { + File file = new File(getDataFolder().getPath(), "mysql.yml"); + Configuration mysql = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); + MySQLConnect.HOST = mysql.getString("HOST"); + MySQLConnect.DATABASE = mysql.getString("DATENBANK"); + MySQLConnect.USER = mysql.getString("USER"); + MySQLConnect.PASSWORD = mysql.getString("PASSWORT"); + } catch (IOException e){ + e.printStackTrace(); + } + mysql = new MySQLConnect(MySQLConnect.HOST, MySQLConnect.DATABASE, MySQLConnect.USER, MySQLConnect.PASSWORD); + mysql.update("CREATE TABLE IF NOT EXISTS accounts(UUID varchar(64) UNIQUE, USERNAME varchar(255), PASSWORD varchar(255), RANK int(11), GOOGLE_AUTH varchar(255), AUTHCODE varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS reasons(ID int(11) UNIQUE, REASON varchar(255), TIME int(255), TYPE int(11), ADDED_AT varchar(11), BANS int(11), PERMS varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS bans(UUID varchar(64) UNIQUE, NAME varchar(64), BANNED int(11), MUTED int(11), REASON varchar(64), END long, TEAMUUID varchar(64), BANS int(11), MUTES int(11), FIRSTLOGIN varchar(255), LASTLOGIN varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS ips(IP varchar(64) UNIQUE, USED_BY varchar(64), USED_AT varchar(64), BANNED int(11), REASON varchar(64), END long, TEAMUUID varchar(64), BANS int(11));"); + mysql.update("CREATE TABLE IF NOT EXISTS reports(ID int(11) AUTO_INCREMENT UNIQUE, UUID varchar(64), REPORTER varchar(64), TEAM varchar(64), REASON varchar(64), LOG varchar(64), STATUS int(11), CREATED_AT long);"); + mysql.update("CREATE TABLE IF NOT EXISTS chat(ID int(11) AUTO_INCREMENT UNIQUE, UUID varchar(64), SERVER varchar(64), MESSAGE varchar(2500), SENDDATE varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS chatlog(ID int(11) AUTO_INCREMENT UNIQUE, LOGID varchar(255), UUID varchar(64), CREATOR_UUID varchar(64), SERVER varchar(64), MESSAGE varchar(2500), SENDDATE varchar(255), CREATED_AT varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS log(ID int(11) AUTO_INCREMENT UNIQUE, UUID varchar(255), BYUUID varchar(255), ACTION varchar(255), NOTE varchar(255), DATE varchar(255));"); + mysql.update("CREATE TABLE IF NOT EXISTS unbans(ID int(11) AUTO_INCREMENT UNIQUE, UUID varchar(255), FAIR int(11), MESSAGE varchar(10000), DATE varchar(255), STATUS int(11));"); + mysql.update("CREATE TABLE IF NOT EXISTS apptokens(UUID varchar(36) UNIQUE, TOKEN varchar(555));"); + //SQL Update 2.0 + mysql.update("ALTER TABLE accounts ADD IF NOT EXISTS AUTHSTATUS int(11);"); + //SQL Update 2.2 + mysql.update("ALTER TABLE bans ADD IF NOT EXISTS FIRSTLOGIN varchar(255);"); + mysql.update("ALTER TABLE bans ADD IF NOT EXISTS LASTLOGIN varchar(255);"); + } + + private void Commands() { + try{ + File file = new File(getDataFolder().getPath(), "config.yml"); + Configuration cfg = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); + getProxy().getPluginManager().registerCommand(this, new Ban("ban")); + getProxy().getPluginManager().registerCommand(this, new Unban("unban")); + getProxy().getPluginManager().registerCommand(this, new Kick("kick")); + getProxy().getPluginManager().registerCommand(this, new WebAccount("webaccount")); + getProxy().getPluginManager().registerCommand(this, new Check("check")); + getProxy().getPluginManager().registerCommand(this, new ProfessionalBans("professionalbans")); + getProxy().getPluginManager().registerCommand(this, new IPBan("ipban")); + if(cfg.getBoolean("REPORTS.ENABLED")){ + getProxy().getPluginManager().registerCommand(this, new Report("report")); + getProxy().getPluginManager().registerCommand(this, new Reports("reports")); + } + if(cfg.getBoolean("CHATLOG.ENABLED")){ + getProxy().getPluginManager().registerCommand(this, new Chatlog("chatlog")); + } + getProxy().getPluginManager().registerCommand(this, new Blacklist("blacklist")); + getProxy().getPluginManager().registerCommand(this, new WebVerify("webverify")); + getProxy().getPluginManager().registerCommand(this, new SupportChat("support")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void Listener() { + getProxy().getPluginManager().registerListener(this, new Login()); + getProxy().getPluginManager().registerListener(this, new Chat()); + getProxy().getPluginManager().registerListener(this, new Quit()); + } + + public static Main getInstance(){ + return main; + } + + public static String callURL(String myURL) { + StringBuilder sb = new StringBuilder(); + URLConnection urlConn = null; + InputStreamReader in = null; + try { + URL url = new URL(myURL); + urlConn = url.openConnection(); + if (urlConn != null) + urlConn.setReadTimeout(60 * 1000); + if (urlConn != null && urlConn.getInputStream() != null) { + in = new InputStreamReader(urlConn.getInputStream(), + Charset.defaultCharset()); + BufferedReader bufferedReader = new BufferedReader(in); + if (bufferedReader != null) { + int cp; + while ((cp = bufferedReader.read()) != -1) { + sb.append((char) cp); + } + bufferedReader.close(); + } + } + in.close(); + } catch (Exception e) { + throw new RuntimeException("Exception while calling URL:"+ myURL, e); + } + + return sb.toString(); + } + +} diff --git a/src/de/tutorialwork/utils/BCrypt.java b/src/de/tutorialwork/utils/BCrypt.java new file mode 100644 index 0000000..500e75e --- /dev/null +++ b/src/de/tutorialwork/utils/BCrypt.java @@ -0,0 +1,752 @@ +// Copyright (c) 2006 Damien Miller +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package de.tutorialwork.utils; + +import java.io.UnsupportedEncodingException; + +import java.security.SecureRandom; + +/** + * BCrypt implements OpenBSD-style Blowfish password hashing using + * the scheme described in "A Future-Adaptable Password Scheme" by + * Niels Provos and David Mazieres. + *

+ * This password hashing system tries to thwart off-line password + * cracking using a computationally-intensive hashing algorithm, + * based on Bruce Schneier's Blowfish cipher. The work factor of + * the algorithm is parameterised, so it can be increased as + * computers get faster. + *

+ * Usage is really simple. To hash a password for the first time, + * call the hashpw method with a random salt, like this: + *

+ * + * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ *
+ *

+ * To check whether a plaintext password matches one that has been + * hashed previously, use the checkpw method: + *

+ * + * if (BCrypt.checkpw(candidate_password, stored_hash))
+ *     System.out.println("It matches");
+ * else
+ *     System.out.println("It does not match");
+ *
+ *

+ * The gensalt() method takes an optional parameter (log_rounds) + * that determines the computational complexity of the hashing: + *

+ * + * String strong_salt = BCrypt.gensalt(10)
+ * String stronger_salt = BCrypt.gensalt(12)
+ *
+ *

+ * The amount of work increases exponentially (2**log_rounds), so + * each increment is twice as much work. The default log_rounds is + * 10, and the valid range is 4 to 31. + * + * @author Damien Miller + * @version 0.2 + */ +public class BCrypt { + // BCrypt parameters + private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private static final int BCRYPT_SALT_LEN = 16; + + // Blowfish parameters + private static final int BLOWFISH_NUM_ROUNDS = 16; + + // Initial contents of key schedule + private static final int P_orig[] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + }; + private static final int S_orig[] = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + }; + + // bcrypt IV: "OrpheanBeholderScryDoubt" + static private final int bf_crypt_ciphertext[] = { + 0x4f727068, 0x65616e42, 0x65686f6c, + 0x64657253, 0x63727944, 0x6f756274 + }; + + // Table for Base64 encoding + static private final char base64_code[] = { + '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9' + }; + + // Table for Base64 decoding + static private final byte index_64[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, + -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + -1, -1, -1, -1, -1, -1, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, -1, -1, -1, -1, -1 + }; + + // Expanded Blowfish key + private int P[]; + private int S[]; + + /** + * Encode a byte array using bcrypt's slightly-modified base64 + * encoding scheme. Note that this is *not* compatible with + * the standard MIME-base64 encoding. + * + * @param d the byte array to encode + * @param len the number of bytes to encode + * @return base64-encoded string + * @exception IllegalArgumentException if the length is invalid + */ + private static String encode_base64(byte d[], int len) + throws IllegalArgumentException { + int off = 0; + StringBuffer rs = new StringBuffer(); + int c1, c2; + + if (len <= 0 || len > d.length) + throw new IllegalArgumentException ("Invalid len"); + + while (off < len) { + c1 = d[off++] & 0xff; + rs.append(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.append(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.append(base64_code[c1 & 0x3f]); + rs.append(base64_code[c2 & 0x3f]); + } + return rs.toString(); + } + + /** + * Look up the 3 bits base64-encoded by the specified character, + * range-checking againt conversion table + * @param x the base64-encoded value + * @return the decoded value of x + */ + private static byte char64(char x) { + if ((int)x < 0 || (int)x > index_64.length) + return -1; + return index_64[(int)x]; + } + + /** + * Decode a string encoded using bcrypt's base64 scheme to a + * byte array. Note that this is *not* compatible with + * the standard MIME-base64 encoding. + * @param s the string to decode + * @param maxolen the maximum number of bytes to decode + * @return an array containing the decoded bytes + * @throws IllegalArgumentException if maxolen is invalid + */ + private static byte[] decode_base64(String s, int maxolen) + throws IllegalArgumentException { + StringBuffer rs = new StringBuffer(); + int off = 0, slen = s.length(), olen = 0; + byte ret[]; + byte c1, c2, c3, c4, o; + + if (maxolen <= 0) + throw new IllegalArgumentException ("Invalid maxolen"); + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 == -1 || c2 == -1) + break; + o = (byte)(c1 << 2); + o |= (c2 & 0x30) >> 4; + rs.append((char)o); + if (++olen >= maxolen || off >= slen) + break; + c3 = char64(s.charAt(off++)); + if (c3 == -1) + break; + o = (byte)((c2 & 0x0f) << 4); + o |= (c3 & 0x3c) >> 2; + rs.append((char)o); + if (++olen >= maxolen || off >= slen) + break; + c4 = char64(s.charAt(off++)); + o = (byte)((c3 & 0x03) << 6); + o |= c4; + rs.append((char)o); + ++olen; + } + + ret = new byte[olen]; + for (off = 0; off < olen; off++) + ret[off] = (byte)rs.charAt(off); + return ret; + } + + /** + * Blowfish encipher a single 64-bit block encoded as + * two 32-bit halves + * @param lr an array containing the two 32-bit half blocks + * @param off the position in the array of the blocks + */ + private final void encipher(int lr[], int off) { + int i, n, l = lr[off], r = lr[off + 1]; + + l ^= P[0]; + for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { + // Feistel substitution on left word + n = S[(l >> 24) & 0xff]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[++i]; + + // Feistel substitution on right word + n = S[(r >> 24) & 0xff]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[++i]; + } + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + } + + /** + * Cycically extract a word of key material + * @param data the string to extract the data from + * @param offp a "pointer" (as a one-entry array) to the + * current offset into data + * @return the next word of material from data + */ + private static int streamtoword(byte data[], int offp[]) { + int i; + int word = 0; + int off = offp[0]; + + for (i = 0; i < 4; i++) { + word = (word << 8) | (data[off] & 0xff); + off = (off + 1) % data.length; + } + + offp[0] = off; + return word; + } + + /** + * Initialise the Blowfish key schedule + */ + private void init_key() { + P = (int[])P_orig.clone(); + S = (int[])S_orig.clone(); + } + + /** + * Key the Blowfish cipher + * @param key an array containing the key + */ + private void key(byte key[]) { + int i; + int koffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the "enhanced key schedule" step described by + * Provos and Mazieres in "A Future-Adaptable Password Scheme" + * http://www.openbsd.org/papers/bcrypt-paper.ps + * @param data salt information + * @param key password information + */ + private void ekskey(byte data[], byte key[]) { + int i; + int koffp[] = { 0 }, doffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the central password hashing step in the + * bcrypt scheme + * @param password the password to hash + * @param salt the binary salt to hash with the password + * @param log_rounds the binary logarithm of the number + * of rounds of hashing to apply + * @return an array containing the binary hashed password + */ + private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { + int rounds, i, j; + int cdata[] = (int[])bf_crypt_ciphertext.clone(); + int clen = cdata.length; + byte ret[]; + + if (log_rounds < 4 || log_rounds > 31) + throw new IllegalArgumentException ("Bad number of rounds"); + rounds = 1 << log_rounds; + if (salt.length != BCRYPT_SALT_LEN) + throw new IllegalArgumentException ("Bad salt length"); + + init_key(); + ekskey(salt, password); + for (i = 0; i < rounds; i++) { + key(password); + key(salt); + } + + for (i = 0; i < 64; i++) { + for (j = 0; j < (clen >> 1); j++) + encipher(cdata, j << 1); + } + + ret = new byte[clen * 4]; + for (i = 0, j = 0; i < clen; i++) { + ret[j++] = (byte)((cdata[i] >> 24) & 0xff); + ret[j++] = (byte)((cdata[i] >> 16) & 0xff); + ret[j++] = (byte)((cdata[i] >> 8) & 0xff); + ret[j++] = (byte)(cdata[i] & 0xff); + } + return ret; + } + + /** + * Hash a password using the OpenBSD bcrypt scheme + * @param password the password to hash + * @param salt the salt to hash with (perhaps generated + * using BCrypt.gensalt) + * @return the hashed password + */ + public static String hashpw(String password, String salt) { + BCrypt B; + String real_salt; + byte passwordb[], saltb[], hashed[]; + char minor = (char)0; + int rounds, off = 0; + StringBuffer rs = new StringBuffer(); + + if (salt.charAt(0) != '$' || salt.charAt(1) != '2') + throw new IllegalArgumentException ("Invalid salt version"); + if (salt.charAt(2) == '$') + off = 3; + else { + minor = salt.charAt(2); + if (minor != 'a' || salt.charAt(3) != '$') + throw new IllegalArgumentException ("Invalid salt revision"); + off = 4; + } + + // Extract number of rounds + if (salt.charAt(off + 2) > '$') + throw new IllegalArgumentException ("Missing salt rounds"); + rounds = Integer.parseInt(salt.substring(off, off + 2)); + + real_salt = salt.substring(off + 3, off + 25); + try { + passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); + } catch (UnsupportedEncodingException uee) { + throw new AssertionError("UTF-8 is not supported"); + } + + saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + + B = new BCrypt(); + hashed = B.crypt_raw(passwordb, saltb, rounds); + + rs.append("$2"); + if (minor >= 'a') + rs.append(minor); + rs.append("$"); + if (rounds < 10) + rs.append("0"); + rs.append(Integer.toString(rounds)); + rs.append("$"); + rs.append(encode_base64(saltb, saltb.length)); + rs.append(encode_base64(hashed, + bf_crypt_ciphertext.length * 4 - 1)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of + * hashing to apply - the work factor therefore increases as + * 2**log_rounds. + * @param random an instance of SecureRandom to use + * @return an encoded salt value + */ + public static String gensalt(int log_rounds, SecureRandom random) { + StringBuffer rs = new StringBuffer(); + byte rnd[] = new byte[BCRYPT_SALT_LEN]; + + random.nextBytes(rnd); + + rs.append("$2a$"); + if (log_rounds < 10) + rs.append("0"); + rs.append(Integer.toString(log_rounds)); + rs.append("$"); + rs.append(encode_base64(rnd, rnd.length)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of + * hashing to apply - the work factor therefore increases as + * 2**log_rounds. + * @return an encoded salt value + */ + public static String gensalt(int log_rounds) { + return gensalt(log_rounds, new SecureRandom()); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method, + * selecting a reasonable default for the number of hashing + * rounds to apply + * @return an encoded salt value + */ + public static String gensalt() { + return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /** + * Check that a plaintext password matches a previously hashed + * one + * @param plaintext the plaintext password to verify + * @param hashed the previously-hashed password + * @return true if the passwords match, false otherwise + */ + public static boolean checkpw(String plaintext, String hashed) { + return (hashed.compareTo(hashpw(plaintext, hashed)) == 0); + } +} \ No newline at end of file diff --git a/src/de/tutorialwork/utils/BanManager.java b/src/de/tutorialwork/utils/BanManager.java new file mode 100644 index 0000000..509827c --- /dev/null +++ b/src/de/tutorialwork/utils/BanManager.java @@ -0,0 +1,640 @@ +package de.tutorialwork.utils; + +import de.tutorialwork.main.Main; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.TimeZone; + +public class BanManager { + + //DATENBANK Struktur + //UUID varchar(64) UNIQUE, NAME varchar(64), BANNED int(11), MUTED int(11), REASON varchar(64), END long(255), TEAMUUID varchar(64), BANS int(11), MUTES int(11) + + public static boolean playerExists(String UUID){ + try { + + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("UUID") != null; + } + + } catch (SQLException exc){ + + } + + return false; + + } + + public static void createPlayer(String UUID, String Name){ + if(!playerExists(UUID)){ + Main.mysql.update("INSERT INTO bans(UUID, NAME, BANNED, MUTED, REASON, END, TEAMUUID, BANS, MUTES, FIRSTLOGIN, LASTLOGIN) " + + "VALUES ('" + UUID + "', '" + Name + "', '0', '0', 'null', 'null', 'null', '0', '0', '" + System.currentTimeMillis() + "', '" + System.currentTimeMillis() + "')"); + } else { + updateName(UUID, Name); + updateLastLogin(UUID); + } + } + + public static String getNameByUUID(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("NAME"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static String getUUIDByName(String Name){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE NAME='" + Name + "'"); + if(rs.next()){ + return rs.getString("UUID"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static void updateName(String UUID, String newName){ + if(playerExists(UUID)){ + Main.mysql.update("UPDATE bans SET NAME='" + newName + "' WHERE UUID='" + UUID + "'"); + } + } + + public static void ban(String UUID, int GrundID, String TeamUUID, int Prozentsatz, boolean increaseBans){ + if(getReasonTime(GrundID) == -1){ + //Perma Ban + Main.mysql.update("UPDATE bans SET BANNED='1', REASON='" + getReasonByID(GrundID) + "', END='-1', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } else { + //Temp Ban + //Formel: 1.50 * Anzahl an Tagen = Ergebniss (50%) + int bans = getBans(UUID); + int defaultmins = getReasonTime(GrundID); + long current = System.currentTimeMillis(); + long end = current + getReasonTime(GrundID) * 60000L; + long increaseEnd = current + Prozentsatz / 100 + 1 * defaultmins * bans * 60000L; //Formel!!!!! + if(increaseBans){ + if(bans > 0){ + Main.mysql.update("UPDATE bans SET BANNED='1', REASON='" + getReasonByID(GrundID) + "', END='" + end + "', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } else { + Main.mysql.update("UPDATE bans SET BANNED='1', REASON='" + getReasonByID(GrundID) + "', END='" + increaseEnd + "', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } + } else { + Main.mysql.update("UPDATE bans SET BANNED='1', REASON='" + getReasonByID(GrundID) + "', END='" + end + "', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } + } + } + + public static void mute(String UUID, int GrundID, String TeamUUID){ + long current = System.currentTimeMillis(); + long end = current + getReasonTime(GrundID) * 60000L; + if(getReasonTime(GrundID) == -1){ + //Perma Mute + Main.mysql.update("UPDATE bans SET MUTED='1', REASON='" + getReasonByID(GrundID) + "', END='-1', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } else { + //Temp Mute + Main.mysql.update("UPDATE bans SET MUTED='1', REASON='" + getReasonByID(GrundID) + "', END='" + end + "', TEAMUUID='" + TeamUUID + "' WHERE UUID='" + UUID + "'"); + } + } + + public static Long getRAWEnd(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getLong("END"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static String getEnd(String UUID) { + long uhrzeit = System.currentTimeMillis(); + long end = getRAWEnd(UUID); + + long millis = end - uhrzeit; + + long sekunden = 0L; + long minuten = 0L; + long stunden = 0L; + long tage = 0L; + while (millis > 1000L) + { + millis -= 1000L; + sekunden += 1L; + } + while (sekunden > 60L) + { + sekunden -= 60L; + minuten += 1L; + } + while (minuten > 60L) + { + minuten -= 60L; + stunden += 1L; + } + while (stunden > 24L) + { + stunden -= 24L; + tage += 1L; + } + if(tage != 0){ + return "§a" + tage + " §7Tag(e) §a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n)"; + } else if(tage == 0 && stunden != 0){ + return "§a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } else if(tage == 0 && stunden == 0 && minuten != 0){ + return "§a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } else if(tage == 0 && stunden == 0 && minuten == 0 && sekunden != 0) { + return "§a" + sekunden + " §7Sekunde(n)"; + } else { + return "§4Fehler in der Berechnung!"; + } + //Alter Code + //return "§a" + tage + " §7Tag(e) §a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } + + public static boolean isBanned(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + if(rs.getInt("BANNED") == 1){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + } + return false; + } + + public static boolean isMuted(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + if(rs.getInt("MUTED") == 1){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + } + return false; + } + + public static void unban(String UUID){ + if(playerExists(UUID)){ + Main.mysql.update("UPDATE bans SET BANNED='0' WHERE UUID='" + UUID + "'"); + } + } + + public static void unmute(String UUID){ + if(playerExists(UUID)){ + Main.mysql.update("UPDATE bans SET MUTED='0' WHERE UUID='" + UUID + "'"); + } + } + + public static String getReasonString(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("REASON"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static void sendNotify(String Type, String BannedName, String TeamName, String Grund){ + if(Type.toUpperCase().equals("BAN")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify")){ + all.sendMessage(Main.Prefix+"§e§l"+BannedName+" §7wurde von §c§l"+TeamName+" §cgebannt §7wegen §a"+Grund); + } + } + } + if(Type.toUpperCase().equals("IPBAN")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify")){ + all.sendMessage(Main.Prefix+"§7Die IP §e§l"+BannedName+" §7wurde von §c§l"+TeamName+" §cgebannt §7wegen §a"+Grund); + } + } + } + if(Type.toUpperCase().equals("MUTE")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§e§l"+BannedName+" §7wurde von §c§l"+TeamName+" §cgemutet §7wegen §a"+Grund); + } + } + } + if(Type.toUpperCase().equals("KICK")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§e§l"+BannedName+" §7wurde von §c§l"+TeamName+" §cgekickt §7wegen §a"+Grund); + } + } + } + if(Type.toUpperCase().equals("UNBAN")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§c§l"+TeamName+" §7hat §e§l"+BannedName+" §aentbannt"); + } + } + } + if(Type.toUpperCase().equals("UNBANIP")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§c§l"+TeamName+" §7hat die IP-Adresse §e§l"+BannedName+" §aentbannt"); + } + } + } + if(Type.toUpperCase().equals("UNMUTE")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§c§l"+TeamName+" §7hat §e§l"+BannedName+" §aentmutet"); + } + } + } + if(Type.toUpperCase().equals("REPORT")){ + for(ProxiedPlayer all : BungeeCord.getInstance().getPlayers()){ + if(all.hasPermission("professionalbans.notify") || all.hasPermission("professionalbans.*")){ + all.sendMessage(Main.Prefix+"§c§l"+TeamName+" §7hat §e§l"+BannedName+" §7wegen §a"+Grund+" §7gemeldet"); + } + } + } + } + + public static Integer getBans(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getInt("BANS"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static void setBans(String UUID, int Bans){ + if(playerExists(UUID)){ + Main.mysql.update("UPDATE bans SET BANS='" + Bans + "' WHERE UUID='" + UUID + "'"); + } + } + + public static Integer getMutes(String UUID){ + if(playerExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getInt("MUTES"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static void setMutes(String UUID, int Mutes){ + if(playerExists(UUID)){ + Main.mysql.update("UPDATE bans SET MUTES='" + Mutes + "' WHERE UUID='" + UUID + "'"); + } + } + + public static Integer countReasons(){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons"); + int i = 0; + while (rs.next()){ + i++; + } + return i; + } catch (SQLException exc){ + + } + return null; + } + + public static String getReasonByID(int Reason){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + Reason + "'"); + if(rs.next()){ + return rs.getString("REASON"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static Integer getReasonTime(int ID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + ID + "'"); + if(rs.next()){ + return rs.getInt("TIME"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static boolean isBanReason(int ID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + ID + "'"); + if(rs.next()){ + if(rs.getInt("TYPE") == 0){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + return false; + } + + public static Integer getReasonBans(int ReasonID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + ReasonID + "'"); + if(rs.next()){ + return rs.getInt("BANS"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static void setReasonBans(int ReasonID, int Bans){ + Main.mysql.update("UPDATE reasons SET BANS='" + Bans + "' WHERE ID='" + ReasonID + "'"); + } + + public static boolean hasExtraPerms(int ReasonID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + ReasonID + "'"); + if(rs.next()){ + if(rs.getString("PERMS").equals("null")){ + return false; + } else { + return true; + } + } + } catch (SQLException exc){ + + } + return false; + } + + public static String getExtraPerms(int ReasonID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reasons WHERE ID='" + ReasonID + "'"); + if(rs.next()){ + return rs.getString("PERMS"); + } + } catch (SQLException exc){ + + } + return null; + } + + + public static boolean webaccountExists(String UUID){ + try { + + ResultSet rs = Main.mysql.query("SELECT * FROM accounts WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("UUID") != null; + } + + } catch (SQLException exc){ + + } + + return false; + + } + + public static void createWebAccount(String UUID, String Name, int Rank, String PasswordHash){ + Main.mysql.update("INSERT INTO accounts(UUID, USERNAME, PASSWORD, RANK, GOOGLE_AUTH, AUTHCODE) " + + "VALUES ('" + UUID + "', '" + Name + "', '" + PasswordHash + "', '" + Rank + "', 'null', 'initialpassword')"); + } + + public static void deleteWebAccount(String UUID){ + Main.mysql.update("DELETE FROM accounts WHERE UUID='"+UUID+"'"); + } + + public static boolean isWebaccountAdmin(String UUID){ + if(webaccountExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM accounts WHERE UUID='" + UUID + "'"); + if(rs.next()){ + if(rs.getInt("RANK") == 3){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + } else { + return false; + } + return false; + } + + public static boolean hasAuthToken(String UUID){ + if(webaccountExists(UUID)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM accounts WHERE UUID='" + UUID + "'"); + if(rs.next()){ + if(rs.getString("AUTHCODE") == "null"){ + return false; + } else { + return true; + } + } + } catch (SQLException exc){ + + } + } else { + return false; + } + return false; + } + + public static String getAuthCode(String UUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM accounts WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("AUTHCODE"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static void updateAuthStatus(String UUID){ + Main.mysql.update("UPDATE accounts SET AUTHSTATUS = 1 WHERE UUID = '"+UUID+"'"); + } + + public static void createReport(String UUID, String ReporterUUID, String Reason, String LogID){ + Main.mysql.update("INSERT INTO reports(UUID, REPORTER, TEAM, REASON, LOG, STATUS, CREATED_AT) " + + "VALUES ('" + UUID + "', '" + ReporterUUID + "', 'null', '" + Reason + "', '" + LogID + "', '0', '" + System.currentTimeMillis() + "')"); + } + + public static Integer countOpenReports(){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE STATUS = 0"); + int i = 0; + while (rs.next()){ + i++; + } + return i; + } catch (SQLException exc){ + + } + return null; + } + + public static ArrayList getIDsFromOpenReports(){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE STATUS = 0"); + ArrayList ids = new ArrayList<>(); + while (rs.next()){ + ids.add(rs.getInt("ID")); + } + return ids; + } catch (SQLException exc){ + + } + return null; + } + + public static String getNameByReportID(int ReportID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE ID='" + ReportID + "'"); + if(rs.next()){ + return getNameByUUID(rs.getString("UUID")); + } + } catch (SQLException exc){ + + } + return null; + } + + public static String getReasonByReportID(int ReportID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE ID='" + ReportID + "'"); + if(rs.next()){ + return rs.getString("REASON"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static void setReportDone(int ID){ + Main.mysql.update("UPDATE reports SET STATUS = 1 WHERE ID = "+ID); + } + + public static void setReportTeamUUID(int ID, String UUID){ + Main.mysql.update("UPDATE reports SET TEAM = '"+UUID+"' WHERE ID = "+ID); + } + + public static boolean isChatlogAvailable(int ID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE ID='" + ID + "'"); + if(rs.next()){ + if(rs.getString("LOG") != "null"){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + return false; + } + + public static String getChatlogbyReportID(int ReportID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM reports WHERE ID='" + ReportID + "'"); + if(rs.next()){ + return rs.getString("LOG"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static void updateLastLogin(String UUID){ + Main.mysql.update("UPDATE bans SET LASTLOGIN = '" + System.currentTimeMillis() + "' WHERE UUID = '"+UUID+"'"); + } + + public static String getLastLogin(String UUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("LASTLOGIN"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static String getFirstLogin(String UUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM bans WHERE UUID='" + UUID + "'"); + if(rs.next()){ + return rs.getString("FIRSTLOGIN"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static String formatTimestamp(long timestamp){ + Date date = new Date(timestamp); + SimpleDateFormat jdf = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + return jdf.format(date); + } + +} diff --git a/src/de/tutorialwork/utils/IPManager.java b/src/de/tutorialwork/utils/IPManager.java new file mode 100644 index 0000000..60e5c7c --- /dev/null +++ b/src/de/tutorialwork/utils/IPManager.java @@ -0,0 +1,250 @@ +package de.tutorialwork.utils; + +import de.tutorialwork.main.Main; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class IPManager { + + //ips(IP varchar(64) UNIQUE, USED_BY varchar(64), USED_AT varchar(64), BANNED int(11), REASON varchar(64), END long, TEAMUUID varchar(64), BANS int(11)); + + public static boolean IPExists(String IP){ + try { + + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return rs.getString("IP") != null; + } + + } catch (SQLException exc){ + + } + + return false; + + } + + public static void insertIP(String IP, String UUID){ + if(!IPExists(IP)){ + Main.mysql.update("INSERT INTO ips(IP, USED_BY, USED_AT, BANNED, REASON, END, TEAMUUID, BANS) " + + "VALUES ('" + IP + "', '" + UUID + "', '" + System.currentTimeMillis() + "', '0', 'null', 'null', 'null', '0')"); + } else { + updateIPInfos(IP, UUID); + } + } + + public static void updateIPInfos(String IP, String newUUID){ + if(IPExists(IP)){ + Main.mysql.update("UPDATE ips SET USED_BY = '"+newUUID+"', USED_AT='" + System.currentTimeMillis() + "' WHERE IP='" + IP + "'"); + } + } + + public static void ban(String IP, int GrundID, String TeamUUID){ + long current = System.currentTimeMillis(); + long end = current + BanManager.getReasonTime(GrundID) * 60000L; + if(BanManager.getReasonTime(GrundID) == -1){ + //Perma Ban + Main.mysql.update("UPDATE ips SET BANNED='1', REASON='" + BanManager.getReasonByID(GrundID) + "', END='-1', TEAMUUID='" + TeamUUID + "' WHERE IP='" + IP + "'"); + } else { + //Temp Ban + Main.mysql.update("UPDATE ips SET BANNED='1', REASON='" + BanManager.getReasonByID(GrundID) + "', END='" + end + "', TEAMUUID='" + TeamUUID + "' WHERE IP='" + IP + "'"); + } + } + + + public static boolean isBanned(String IP){ + if(IPExists(IP)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + if(rs.getInt("BANNED") == 1){ + return true; + } else { + return false; + } + } + } catch (SQLException exc){ + + } + } + return false; + } + + public static String getReasonString(String IP){ + if(IPExists(IP)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return rs.getString("REASON"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static Long getRAWEnd(String IP){ + if(IPExists(IP)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return rs.getLong("END"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static String getEnd(String UUID) { + long uhrzeit = System.currentTimeMillis(); + long end = getRAWEnd(UUID); + + long millis = end - uhrzeit; + + long sekunden = 0L; + long minuten = 0L; + long stunden = 0L; + long tage = 0L; + while (millis > 1000L) + { + millis -= 1000L; + sekunden += 1L; + } + while (sekunden > 60L) + { + sekunden -= 60L; + minuten += 1L; + } + while (minuten > 60L) + { + minuten -= 60L; + stunden += 1L; + } + while (stunden > 24L) + { + stunden -= 24L; + tage += 1L; + } + if(tage != 0){ + return "§a" + tage + " §7Tag(e) §a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n)"; + } else if(tage == 0 && stunden != 0){ + return "§a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } else if(tage == 0 && stunden == 0 && minuten != 0){ + return "§a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } else if(tage == 0 && stunden == 0 && minuten == 0 && sekunden != 0) { + return "§a" + sekunden + " §7Sekunde(n)"; + } else { + return "§4Fehler in der Berechnung!"; + } + //Alter Code + //return "§a" + tage + " §7Tag(e) §a" + stunden + " §7Stunde(n) §a" + minuten + " §7Minute(n) §a" + sekunden + " §7Sekunde(n)"; + } + + public static void unban(String IP){ + if(IPExists(IP)){ + Main.mysql.update("UPDATE ips SET BANNED='0' WHERE IP='" + IP + "'"); + } + } + + public static boolean isVPN(String IP){ + if(!IP.equals("127.0.0.1")){ + if(Main.APIKey != null){ + String json = Main.callURL("http://proxycheck.io/v2/"+IP+"?key="+Main.APIKey); + json = json.replace("{\n" + + " \"status\": \"ok\",\n" + + " \""+IP+"\": {\n" + + " \"proxy\": \"", ""); + json = json.replace("\"\n" + + " }\n" + + "}", ""); + if(json.equals("yes")){ + return true; + } else { + return false; + } + } else { + String json = Main.callURL("http://proxycheck.io/v2/"+IP+"?key=318n07-0o7054-y9y82a-75o3hr"); + json = json.replace("{\n" + + " \"status\": \"ok\",\n" + + " \""+IP+"\": {\n" + + " \"proxy\": \"", ""); + json = json.replace("\"\n" + + " }\n" + + "}", ""); + if(json.equals("yes")){ + return true; + } else { + return false; + } + } + } else { + return false; + } + } + + public static String getIPFromPlayer(String UUID){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE USED_BY='" + UUID + "'"); + if(rs.next()){ + return rs.getString("IP"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static String getPlayerFromIP(String IP){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return rs.getString("USED_BY"); + } + } catch (SQLException exc){ + + } + return null; + } + + public static Integer getBans(String IP){ + if(IPExists(IP)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return rs.getInt("BANS"); + } + } catch (SQLException exc){ + + } + } + return null; + } + + public static void setBans(String IP, int Bans){ + if(IPExists(IP)){ + Main.mysql.update("UPDATE ips SET BANS='" + Bans + "' WHERE IP='" + IP + "'"); + } + } + + public static void addBan(String IP){ + setBans(IP, getBans(IP) + 1); + } + + public static long getLastUseLong(String IP){ + if(IPExists(IP)){ + try { + ResultSet rs = Main.mysql.query("SELECT * FROM ips WHERE IP='" + IP + "'"); + if(rs.next()){ + return Long.valueOf(rs.getString("USED_AT")); + } + } catch (SQLException exc){ + + } + } + return 0; + } + +} diff --git a/src/de/tutorialwork/utils/LogManager.java b/src/de/tutorialwork/utils/LogManager.java new file mode 100644 index 0000000..fa5ef85 --- /dev/null +++ b/src/de/tutorialwork/utils/LogManager.java @@ -0,0 +1,21 @@ +package de.tutorialwork.utils; + +import de.tutorialwork.main.Main; + +public class LogManager { + + //DATABASE STRUCTURE + //ID int(11) AUTO_INCREMENT UNIQUE, UUID varchar(255), BYUUID varchar(255), ACTION varchar(255), NOTE varchar(255), DATE varchar(255) + + //ACTION Codes + //BAN, MUTE, ADD_WORD_BLACKLIST, DEL_WORD_BLACKLIST, CREATE_CHATLOG, IPBAN_IP, IPBAN_PLAYER, KICK, REPORT, REPORT_OFFLINE, REPORT_ACCEPT, UNBAN_IP, UNBAN_BAN, UNBAN_MUTE, + // ADD_WEBACCOUNT, DEL_WEBACCOUNT, AUTOMUTE_ADBLACKLIST, AUTOMUTE_BLACKLIST + // + //UUID/BY_UUID = UUID des Spielers, null = keine Spieler verfügbar, "KONSOLE" = Befehl über Konsole ausgeführt + + public static void createEntry(String UUID, String ByUUID, String Action, String Note){ + Main.mysql.update("INSERT INTO log(UUID, BYUUID, ACTION, NOTE, DATE) " + + "VALUES ('" + UUID + "', '" + ByUUID + "', '" + Action + "', '" + Note + "', '" + System.currentTimeMillis() + "')"); + } + +} diff --git a/src/de/tutorialwork/utils/Metrics.java b/src/de/tutorialwork/utils/Metrics.java new file mode 100644 index 0000000..c5dceec --- /dev/null +++ b/src/de/tutorialwork/utils/Metrics.java @@ -0,0 +1,754 @@ +package de.tutorialwork.utils; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.md_5.bungee.api.plugin.Plugin; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + *

+ * Check out https://bStats.org/ to learn more about bStats! + */ +@SuppressWarnings({"WeakerAccess", "unused"}) +public class Metrics { + + static { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String( + new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'n', 'g', 'e', 'e', 'c', 'o', 'r', 'd'}); + final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bungeecord"; + + // The plugin + private final Plugin plugin; + + // Is bStats enabled on this server? + private boolean enabled; + + // The uuid of the server + private String serverUUID; + + // Should failed requests be logged? + private boolean logFailedRequests = false; + + // Should the sent data be logged? + private static boolean logSentData; + + // Should the response text be logged? + private static boolean logResponseStatusText; + + // A list with all known metrics class objects including this one + private static final List knownMetricsInstances = new ArrayList<>(); + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + public Metrics(Plugin plugin) { + this.plugin = plugin; + + try { + loadConfig(); + } catch (IOException e) { + // Failed to load configuration + plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e); + return; + } + + // We are not allowed to send data about this server :( + if (!enabled) { + return; + } + + Class usedMetricsClass = getFirstBStatsClass(); + if (usedMetricsClass == null) { + // Failed to get first metrics class + return; + } + if (usedMetricsClass == getClass()) { + // We are the first! :) + linkMetrics(this); + startSubmitting(); + } else { + // We aren't the first so we link to the first metrics class + try { + usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null, this); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Failed to link to first metrics class " + usedMetricsClass.getName() + "!", e); + } + } + } + } + + /** + * Checks if bStats is enabled. + * + * @return Whether bStats is enabled or not. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + plugin.getLogger().log(Level.WARNING, "Chart cannot be null"); + } + charts.add(chart); + } + + /** + * Links an other metrics class with this class. + * This method is called using Reflection. + * + * @param metrics An object of the metrics class to link. + */ + public static void linkMetrics(Object metrics) { + knownMetricsInstances.add(metrics); + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JsonObject getPluginData() { + JsonObject data = new JsonObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.addProperty("pluginName", pluginName); + data.addProperty("pluginVersion", pluginVersion); + + JsonArray customCharts = new JsonArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JsonObject chart = customChart.getRequestJsonObject(plugin.getLogger(), logFailedRequests); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.add("customCharts", customCharts); + + return data; + } + + private void startSubmitting() { + // The data collection is async, as well as sending the data + // Bungeecord does not have a main thread, everything is async + plugin.getProxy().getScheduler().schedule(plugin, this::submitData, 2, 30, TimeUnit.MINUTES); + // Submit the data every 30 minutes, first time after 2 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JsonObject getServerData() { + // Minecraft specific data + int playerAmount = plugin.getProxy().getOnlineCount(); + playerAmount = playerAmount > 500 ? 500 : playerAmount; + int onlineMode = plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0; + String bungeecordVersion = plugin.getProxy().getVersion(); + int managedServers = plugin.getProxy().getServers().size(); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JsonObject data = new JsonObject(); + + data.addProperty("serverUUID", serverUUID); + + data.addProperty("playerAmount", playerAmount); + data.addProperty("managedServers", managedServers); + data.addProperty("onlineMode", onlineMode); + data.addProperty("bungeecordVersion", bungeecordVersion); + + data.addProperty("javaVersion", javaVersion); + data.addProperty("osName", osName); + data.addProperty("osArch", osArch); + data.addProperty("osVersion", osVersion); + data.addProperty("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JsonObject data = getServerData(); + + final JsonArray pluginData = new JsonArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Object metrics : knownMetricsInstances) { + try { + Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics); + if (plugin instanceof JsonObject) { + pluginData.add((JsonObject) plugin); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + + data.add("plugins", pluginData); + + try { + // Send the data + sendData(plugin, data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e); + } + } + } + + /** + * Loads the bStats configuration. + * + * @throws IOException If something did not work :( + */ + private void loadConfig() throws IOException { + Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); + configPath.toFile().mkdirs(); + File configFile = new File(configPath.toFile(), "config.yml"); + if (!configFile.exists()) { + writeFile(configFile, + "#bStats collects some data for plugin authors like how many servers are using their plugins.", + "#To honor their work, you should not disable it.", + "#This has nearly no effect on the server performance!", + "#Check out https://bStats.org/ to learn more :)", + "enabled: true", + "serverUuid: \"" + UUID.randomUUID().toString() + "\"", + "logFailedRequests: false", + "logSentData: false", + "logResponseStatusText: false"); + } + + Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); + + // Load configuration + enabled = configuration.getBoolean("enabled", true); + serverUUID = configuration.getString("serverUuid"); + logFailedRequests = configuration.getBoolean("logFailedRequests", false); + logSentData = configuration.getBoolean("logSentData", false); + logResponseStatusText = configuration.getBoolean("logResponseStatusText", false); + } + + /** + * Gets the first bStat Metrics class. + * + * @return The first bStats metrics class. + */ + private Class getFirstBStatsClass() { + Path configPath = plugin.getDataFolder().toPath().getParent().resolve("bStats"); + configPath.toFile().mkdirs(); + File tempFile = new File(configPath.toFile(), "temp.txt"); + + try { + String className = readFile(tempFile); + if (className != null) { + try { + // Let's check if a class with the given name exists. + return Class.forName(className); + } catch (ClassNotFoundException ignored) { } + } + writeFile(tempFile, getClass().getName()); + return getClass(); + } catch (IOException e) { + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e); + } + return null; + } + } + + /** + * Reads the first line of the file. + * + * @param file The file to read. Cannot be null. + * @return The first line of the file or null if the file does not exist or is empty. + * @throws IOException If something did not work :( + */ + private String readFile(File file) throws IOException { + if (!file.exists()) { + return null; + } + try ( + FileReader fileReader = new FileReader(file); + BufferedReader bufferedReader = new BufferedReader(fileReader); + ) { + return bufferedReader.readLine(); + } + } + + /** + * Writes a String to a file. It also adds a note for the user, + * + * @param file The file to write to. Cannot be null. + * @param lines The lines to write. + * @throws IOException If something did not work :( + */ + private void writeFile(File file, String... lines) throws IOException { + if (!file.exists()) { + file.createNewFile(); + } + try ( + FileWriter fileWriter = new FileWriter(file); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter) + ) { + for (String line : lines) { + bufferedWriter.write(line); + bufferedWriter.newLine(); + } + } + } + + /** + * Sends the data to the bStats server. + * + * @param plugin Any plugin. It's just used to get a logger instance. + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(Plugin plugin, JsonObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null"); + } + if (logSentData) { + plugin.getLogger().info("Sending data to bStats: " + data.toString()); + } + + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + InputStream inputStream = connection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + bufferedReader.close(); + if (logResponseStatusText) { + plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString()); + } + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + gzip.close(); + return outputStream.toByteArray(); + } + + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + private final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JsonObject getRequestJsonObject(Logger logger, boolean logFailedRequests) { + JsonObject chart = new JsonObject(); + chart.addProperty("chartId", chartId); + try { + JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.add("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JsonObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.addProperty("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + JsonObject values = new JsonObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.addProperty(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.add("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + JsonObject values = new JsonObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JsonObject value = new JsonObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.addProperty(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.add(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.add("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.addProperty("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + JsonObject values = new JsonObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.addProperty(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.add("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + JsonObject values = new JsonObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JsonArray categoryValues = new JsonArray(); + categoryValues.add(new JsonPrimitive(entry.getValue())); + values.add(entry.getKey(), categoryValues); + } + data.add("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + JsonObject values = new JsonObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JsonArray categoryValues = new JsonArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(new JsonPrimitive(categoryValue)); + } + values.add(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.add("values", values); + return data; + } + + } + +} \ No newline at end of file diff --git a/src/de/tutorialwork/utils/MySQLConnect.java b/src/de/tutorialwork/utils/MySQLConnect.java new file mode 100644 index 0000000..93fa340 --- /dev/null +++ b/src/de/tutorialwork/utils/MySQLConnect.java @@ -0,0 +1,71 @@ +package de.tutorialwork.utils; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import de.tutorialwork.main.Main; +import net.md_5.bungee.BungeeCord; + +public class MySQLConnect { + + public static String HOST; + public static String DATABASE; + public static String USER; + public static String PASSWORD; + + private Connection con; + + public MySQLConnect(String host, String database, String user, String password) { + this.HOST = host; + this.DATABASE = database; + this.USER = user; + this.PASSWORD = password; + + connect(); + } + + public void connect() { + try { + con = DriverManager.getConnection("jdbc:mysql://" + HOST + ":3306/" + DATABASE + "?autoReconnect=true", USER, PASSWORD); + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§aDie Verbindung mit der MySQL Datenbank wurde erfolgreich hergestellt"); + } catch (SQLException e) { + BungeeCord.getInstance().getConsole().sendMessage(Main.Prefix+"§cDie Verbindung mit der MySQL Datenbank ist fehlgeschlagen: §4" + e.getMessage()); + } + } + + public void close() { + try { + if(con != null) { + con.close(); + } + } catch (SQLException e) { + } + } + + public void update(String qry) { + try { + Statement st = con.createStatement(); + st.executeUpdate(qry); + st.close(); + } catch (SQLException e) { + connect(); + System.err.println(e); + } + } + + public ResultSet query(String qry) { + ResultSet rs = null; + + try { + Statement st = con.createStatement(); + rs = st.executeQuery(qry); + } catch (SQLException e) { + connect(); + System.err.println(e); + } + return rs; + } +} \ No newline at end of file diff --git a/src/de/tutorialwork/utils/RandomString.java b/src/de/tutorialwork/utils/RandomString.java new file mode 100644 index 0000000..c045a80 --- /dev/null +++ b/src/de/tutorialwork/utils/RandomString.java @@ -0,0 +1,22 @@ +package de.tutorialwork.utils; + +import java.security.SecureRandom; + +public class RandomString { + private static final String ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"; + private static final SecureRandom RANDOM = new SecureRandom(); + + /** + * Generates random string of given length from Base65 alphabet (numbers, lowercase letters, uppercase letters). + * + * @param count length + * @return random string of given length + */ + public static String generate(int count) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; ++i) { + sb.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length()))); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/de/tutorialwork/utils/UUIDFetcher.java b/src/de/tutorialwork/utils/UUIDFetcher.java new file mode 100644 index 0000000..5440784 --- /dev/null +++ b/src/de/tutorialwork/utils/UUIDFetcher.java @@ -0,0 +1,48 @@ +package de.tutorialwork.utils; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; + +public class UUIDFetcher +{ + private static HashMap uuidCache = new HashMap(); + + public static String getUUID(String username) + { + if (uuidCache.containsKey(username)) { + return (String)uuidCache.get(username); + } + try + { + URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + username); + InputStream stream = url.openStream(); + InputStreamReader inr = new InputStreamReader(stream); + BufferedReader reader = new BufferedReader(inr); + String s = null; + StringBuilder sb = new StringBuilder(); + while ((s = reader.readLine()) != null) { + sb.append(s); + } + String result = sb.toString(); + JsonElement element = new JsonParser().parse(result); + JsonObject obj = element.getAsJsonObject(); + String api = obj.get("id").toString(); + api = api.substring(1); + api = api.substring(0, api.length() - 1); + StringBuffer sbu = new StringBuffer(api); + sbu.insert(8, "-").insert(13, "-").insert(18, "-").insert(23, "-"); + String uuid = sbu.toString(); + uuidCache.put(username, uuid); + return uuid; + } + catch (IOException|IllegalStateException localIOException) {} + return null; + } +} \ No newline at end of file diff --git a/src/plugin.yml b/src/plugin.yml new file mode 100644 index 0000000..200f415 --- /dev/null +++ b/src/plugin.yml @@ -0,0 +1,4 @@ +name: ProfessionalBans Reloaded +author: Tutorialwork +version: 2.3 +main: de.tutorialwork.main.Main \ No newline at end of file