From 1a0344d0e8ca018e12d4e80c0c0eeec77ba2a640 Mon Sep 17 00:00:00 2001 From: KatykhinAA Date: Sun, 14 Jul 2024 21:22:33 +0300 Subject: [PATCH] migrate to aiogram 3.10.0 --- .gitignore | 1 + Stick/Hello_11.webp | Bin 0 -> 26586 bytes Stick/Hello_12.webp | Bin 0 -> 16604 bytes Stick/Hello_13.webp | Bin 0 -> 23666 bytes Stick/Universal_11.webp | Bin 0 -> 32894 bytes helper_bot/__init__.py | 0 helper_bot/filters/main.py | 15 + helper_bot/handlers/admin/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 217 bytes .../admin/__pycache__/main.cpython-312.pyc | Bin 0 -> 11097 bytes helper_bot/handlers/admin/main.py | 143 ++++ helper_bot/handlers/callback/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 223 bytes .../callback/__pycache__/main.cpython-312.pyc | Bin 0 -> 15076 bytes helper_bot/handlers/callback/main.py | 164 ++++ helper_bot/handlers/group/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 217 bytes .../group/__pycache__/main.cpython-312.pyc | Bin 0 -> 4958 bytes helper_bot/handlers/group/main.py | 54 ++ helper_bot/handlers/private/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 221 bytes .../private/__pycache__/main.cpython-312.pyc | Bin 0 -> 23098 bytes helper_bot/handlers/private/main.py | 281 +++++++ helper_bot/helper_bot.py | 793 ------------------ helper_bot/keyboards/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 259 bytes .../__pycache__/main.cpython-312.pyc | Bin 0 -> 6436 bytes helper_bot/keyboards/main.py | 110 +++ helper_bot/main.py | 21 + helper_bot/middlewares/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 170 bytes .../text_middleware.cpython-312.pyc | Bin 0 -> 2735 bytes helper_bot/middlewares/album_middleware.py | 46 + helper_bot/middlewares/text_middleware.py | 61 ++ helper_bot/utils/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 206 bytes .../base_dependency_factory.cpython-312.pyc | Bin 0 -> 1802 bytes .../__pycache__/helper_func.cpython-312.pyc | Bin 0 -> 8650 bytes .../__pycache__/messages.cpython-312.pyc | Bin 0 -> 5461 bytes .../utils/__pycache__/state.cpython-312.pyc | Bin 0 -> 673 bytes helper_bot/utils/base_dependency_factory.py | 25 + helper_bot/utils/helper_func.py | 191 +++++ messages.py => helper_bot/utils/messages.py | 0 helper_bot/utils/state.py | 13 + main.py | 50 -- requirements.txt | 10 +- run_helper.py | 7 + 47 files changed, 1145 insertions(+), 846 deletions(-) create mode 100644 .gitignore create mode 100644 Stick/Hello_11.webp create mode 100644 Stick/Hello_12.webp create mode 100644 Stick/Hello_13.webp create mode 100644 Stick/Universal_11.webp create mode 100644 helper_bot/__init__.py create mode 100644 helper_bot/filters/main.py create mode 100644 helper_bot/handlers/admin/__init__.py create mode 100644 helper_bot/handlers/admin/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/handlers/admin/__pycache__/main.cpython-312.pyc create mode 100644 helper_bot/handlers/admin/main.py create mode 100644 helper_bot/handlers/callback/__init__.py create mode 100644 helper_bot/handlers/callback/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/handlers/callback/__pycache__/main.cpython-312.pyc create mode 100644 helper_bot/handlers/callback/main.py create mode 100644 helper_bot/handlers/group/__init__.py create mode 100644 helper_bot/handlers/group/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/handlers/group/__pycache__/main.cpython-312.pyc create mode 100644 helper_bot/handlers/group/main.py create mode 100644 helper_bot/handlers/private/__init__.py create mode 100644 helper_bot/handlers/private/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/handlers/private/__pycache__/main.cpython-312.pyc create mode 100644 helper_bot/handlers/private/main.py delete mode 100644 helper_bot/helper_bot.py create mode 100644 helper_bot/keyboards/__init__.py create mode 100644 helper_bot/keyboards/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/keyboards/__pycache__/main.cpython-312.pyc create mode 100644 helper_bot/keyboards/main.py create mode 100644 helper_bot/main.py create mode 100644 helper_bot/middlewares/__init__.py create mode 100644 helper_bot/middlewares/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/middlewares/__pycache__/text_middleware.cpython-312.pyc create mode 100644 helper_bot/middlewares/album_middleware.py create mode 100644 helper_bot/middlewares/text_middleware.py create mode 100644 helper_bot/utils/__init__.py create mode 100644 helper_bot/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 helper_bot/utils/__pycache__/base_dependency_factory.cpython-312.pyc create mode 100644 helper_bot/utils/__pycache__/helper_func.cpython-312.pyc create mode 100644 helper_bot/utils/__pycache__/messages.cpython-312.pyc create mode 100644 helper_bot/utils/__pycache__/state.cpython-312.pyc create mode 100644 helper_bot/utils/base_dependency_factory.py create mode 100644 helper_bot/utils/helper_func.py rename messages.py => helper_bot/utils/messages.py (100%) create mode 100644 helper_bot/utils/state.py delete mode 100644 main.py create mode 100644 run_helper.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cf4112 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/database/tg-bot-database diff --git a/Stick/Hello_11.webp b/Stick/Hello_11.webp new file mode 100644 index 0000000000000000000000000000000000000000..8c4d84a5203d3f46161a4bda33657f7f8165b841 GIT binary patch literal 26586 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℞1%Jv8yhPY1|4iyHRp@;kPNMxt_D2 z9Kw>CN0m>pxAsBPY|}^QJ#Q*+BcJsk+3XY)95_1`_GiXGrxi{4yUd8KDp!T$j@Z%) zam@i8aB%@EImkhFK{}64!<$Qf-5^S1NQX<^uJ+}4KL{XU%@K~8S?OmS%YsjW_zelm zH_r&>zutNB$}s97ln! zvE#@^FDc$>v!NbGM2(ucj4d9h-52?qYhjo;wK!tvV>ilg3+@A5I7EBDCQGlGUN{{58NMh~zWkvZN}UeN{h{?^oBxw1KOa+jL_7<@`wm(?BBs z#G`5JtM57rPPXlXkcgqc4Uv zor*lEO<>}%XHu$Nn=+5jZb|(o#q9!2Zwm|sq!^#*jzy-Ow2EcjC0#Ft z&jb`+^l_Uu^C@!H9qh29P^BlRBF%`kd3j5EaF(WF_t&kUY4_0VH1BO~*;0s?OkuSA z19xlOKQ|T*GH$Sbc$|;+_SspUKw#De`_Uc&RwH$lot!I}m(@AML~oKE1Fu#~0D;y1 z6mx(dfT{a+n~V)rBbbq3w(jXT@ZXTe7}pMjCx*ZFy9U@W0k9 zehkbUz9_6ZLX&*SYHd5Bb)uZ0!~J=d*)RE)zh}nXP{#x5pV~LH)z4LlvmRRu7Fao^ zXTps)-U1nwZ)VA zZ&WiVC6Q$|_e573TDAn0P(^{@tJK3<;avt7Up;#p0lscy6y-Q5DRJkGuH&cSYF1r} z`|c?yR|kxITE%7v9cPm$y&L=lcISNqSsUO7b`qkP-`|Gba_0AlCpeRk_A|Y!e@Ofb#h2os9{pGh_l3OU zQ@nmLa_qiz4{V!WrtO;o{LaciSe_uZJ#t5KfeLz-<-@eRa;K*9YoVZi8|#~SJAI6L zddFsJIq$&EJ>ryL%gMGgi}eRDAq@|d=bhtbStRB@c3ml=x0^=G?Rxsq({O*$1Hl9Z!ks$m zd^15e!guAvW*4KXKQu^i@ew~;`R6ODvEt6aP2owjOYhPC%N`*JLAx|%7-~FvQS?s9 z#XK)+^2vV|AG64y8^Ew#(B@dnM$E*oE4Ns!bN~4IID6W8(!cXhq{+Gyq3#W(Dwphp zA$q(HmhB9QwbbNA2?VxXc*)+WiOK7#+Kvg5LDg8T*UehI>DIb%f$ZecEE>d(iVzilt(fA4wECKRp{o#-fWJRK+1CL>4`^zW|Kg>$|Mk(CNo`2f8AB&``Xs- zQb6+1Ma9zN>dwR*5!O^8$1UsTxHnrj8}hr756}=BnhK6Y3DF!PgoV%oi?Alm|5#%& z8f`(otzeUA^@&D+!AxCm2!j7b;`K8wtkGJOHuH9Ut|8ub^p1V>=|>-(>LOa@5gqX-GhId-nEZ5Q%u3l^braZ`^a2-0AIa2_9CD!*A_!`=OS0vN zdEZhRpQ|;_+k-@1K-JY`={F@U5#P9eGimgj&5L&j*6*oKbQn~(v{1J@%b;w$(8rkt zwZ%sggUbv+R^tD7iudD|N7&<0@#A2g3&dcr+h%IgT8H5oHiR>tMn0zZBLGqJ!)>SSxO+eubKehxv@b5}}7zcg>2(lyaeJbLt7;Bol{B3}^T2Lt!t&V}6zvX)%WO;A243 zSSKyQ_hAg+ve!V`*>hh!XP*{+JM^NCNwpZKrPTrrFNX+e$_|p1M)Xb|R~%7Sxy+vp z^iwcHdLH}!tQ7Z05N>-0v7GoKE82j*Ak(g{;qZL%6h5g{56;s z;pJa(iO}e5D3``$vfzv9)p5Uo|Gm^AN<+CM5QY>7kbZ2w4wZ6V7DYh88?^vqEejY@ z*pxZ;MA3tcz$<3dV?{`ZmbZ*=vK4+cP;{an1X-4^UVCFWV4XOlyiT3>4akoE`L(;6W&brr&omw) zAwVG#l$+sYnf9r>Iq}4Kd&c`$@XXKkWC@n6O@sa2iDrkuN^KeZoT{7Xs3-gdUJ6W1 zlgin(2<3J9dmI)97H`}e)BbuDkzz~*1Y)9l_$SK^cjA$v*P*+-4?^zD6v(jkGdZb$x)Z~00~eL_;Cr0{ z9=Hla+s~7~8LfdeN46B7R!vzNklUW=C@oMSaZ61BoF39N(0wKN#e!?|OAUngruG7F zk**|SA5;t1k*t%r3t7}l6jOos`Kx6hAns<#eSDTi)G3(gLMnlrNl9DX zf_0u|$=9YCMiG!3#W5*j5%D1HI76~<63#=LT_Nx4gnzA$U&J+)L;aK`^+wL*xxauS zi%9^KUuT_NGD?bCyZ;ez0;fP4;peRE;RMYV18M9wi0)ol!3>LeE=yC6YU_BoqA1F` z!$)*%ns86U^JmoExHcbMjt@arZCEY{e=-&=mJJ){MmW^!4Y>`6P40@&%y?JQLTf*8 zU!7XYf0c?y_3i!~(^nBVu+)O_!e~C!1H52p7i~}70!be?pOb>Pn=`6Cox(NZGa$~g zQ1M6teF(eaLQ;R`XMA$Rl!7w*dSxx8{B)dU%>B}lhmqbeMk7lNeGz2yu&^?c;o`JyIZnC*H}poCjong8{y?;AF9tYb zR{6V_Q){ZH-*M9yMRv*@HfqL^Ri{&Nf)@$z#0`4844r$w2|cV^MA`|BM^J;GS3C3O z9ttG4@fz(j_cyp`oLj-o5fB?u*j)GdAVpPozJP9fg2Dj*cilJ1AlQwi#2q>2%P zsS;ViPuw=sn?cqB-16h1#`+x+Cd1_SuX&4NZ@Kp2N#b}QIdZB6d)QlLXP+1E+C-UP znIo!PmEBAnKMl!?=RUZs5>53-Tw9zC&HgYKki=4?s{mk4L#7IjP2@-s%1*xh6GusP zBCK{dIqAdhmikcp4TLnJ<`jzOn?Z&kjiBagemM&~EPkFgBMZ6;#*gHjIJR<;36d)r;Fv#S)}jZ1 z%JphkJ+MDc!wOjxN@2b&*TY%BT>_o`4V+Nji5^2K zuM3aLoov$JV{g(($I~A>hx{F6w}ANPU{w)b{9lIl^Dfqo{!gYeQ<+){ClC{k^tiuS=^U=`=C4}*@D7LLd|Sof7zg(IaOQ$A zMvD_1WP1#`4;o9?0CA90P(5g<KCnbaARVqC@0sesc^*4DEUir=!MD}HgGNecR zEM<8tr_M_ch0uljizGbUC-3B@$20$$LP%>n1=w&Q!Q6c41`*rRuH=IzV&JDohV zVoI;Yv%2`_GXeW5FSct$mc8rd?2)yTF4-Sawl|yDmxSX;5BzC6i2T*%lRPPb{C8uXik)OhfS3-7jX$2RVNz$*Nb-d1uBteca;bG-V3p zi$m4=!1QbRlahpkQaCNk`D5sfg}TQx4y*l?hBh#U0jMSX+wzfcuLLK|b{jEGDbWox zlvo1k2DZo?%ua%$2N5gp{hxh`5mLmI!9jK`hoZlbzG+<9jdDB3EC8MM%V_7^eGTuT zGtd@$L^auIs_njy%_!!ZX6vx_N{f=6R^gj9`}U6Te4rgXxqq7iZ2*1CC*T+L-@?gb9t5tvWRo)B zrr*jLs=R;uEI{rirmM>h+*@c_&y~V0-EcwnGG6!68sH=mx;CYkI1jWSjk`K`Eqb;(CvQ>U-I~ZO6>jXbp2G5XDpZG+7AupU$YxhO%I{q`toU!!5 zX7j3u~KS^`y;&9e$RWs+Gx zxz%Rg0SpTE17Y!|^n=jmghwdFl{d;97zk9b!D7A?6Fkg)3 zBw)^y;=WyVghVYDfHwExCItb0)+z{1%Vr$(&S0@+IelVU}C zyE>Gb*rLq<@hQEaW0m-re<|Rhvj*lAulNBm=umAk#A$Sb8DiSgu11Roc`bJVrUwG& z^V z<&-F2%xLfLth=?$ZbTgcFB_xT>7_=ndUdJZh0EAymn_{3f0$ zL|gjkGs&oFE4fZBf60Cl;LZvv1HyH}B%)0tTZ|lZEYFrY+g&mF)D3os=eCti8`lU~|k5-c5gQP~sl`F9P=j5>3h9oUk z2)F{R)33BRL!sJ*!IYr$oTqYrdZbe&YvuKWmS0nT!pK1xWEJuj!EK*#y}Jml(#^+e z#Po`y5mFfr7zBnXD!1Htet<~{TjZDC7j@uu{ zdCw+Xf-tf0R`I9OldYilWR;{6l^#j0Fj1PeHBVof|B{d&sW0LRw=R&z5Z&hbm{wBI z$ZC5yHT^EQlkaEJgG!M7_i!9nA7_i4)PB6QQh!+bA)#DS2833d2+9`f-7#%-F#<)kWmicBH#`wFK~3@y+5J zrH}uNbT!Kgn?@ug&iISdDi_}g?)L>+^&)DtM`}JbAm%@bdXr$PK65x+(nCbb{>ZD3 zcvO18J+LhFiko-UTAAHfNz`Ecjc`aY2i3~UUz?)!6GQUPi%U3nE|tS=pp&7`o*pwl zf&I_2CEwsOb?l#)SevjUE-7HYK=>x@f;~Hb{Tx&D~SSrZL_RQL@>E%%K1?X{N zD4*aCfAL(wYETF~Y|gXYUYMc-e2F*uofob=vLb=%ma()BqH`Kj^s?Q@>U)NHa2+&` z>k;FVX9giDL{(-3jl5yJE;ob4NcpkMel0j$Sz+BJB4Si>UMy|?alCMG@8@>e$77wj zc>-_w)&_OdV!2IH9SbTa-y5^G-b@yM^WA2uC{!OoCFMMFZCdkoQeS|8;q)SHa8wID zO2m&1i(v@O(%KBonLZ5MMvvJ3`;1riC$auq)ZJJiPb{hFnGn&&sI0j<82K8=alo)eyD@{Nda6 zIzeZH+<$GNDV4dTWQmTw${fL1wh25uc2yzMu!DN{*?Bf;-YsXUKhLf7=wC1`Ryr<<4C(=!-{=60ZVfI1yx*+ha*~+y1C;Ja$8Qq;YvMIIwi6LyG z7ZS(&=KQmi4=nky1WX{dkmvSTTq%L>%gekqA*_xY6xhsMwlpyZO;qZRYEm$p3!k+M933cZYQIzlQ{thDr@zH<;lPqT1m3E^ z3p115!X?aTrE$G8iDmjkIJq3%xvLb#6?twW0)BFEyqQcEH0L3p?fGPcRCfseaTZY; zBTvPaN3>jXKVon6C(WaF63~JSO;d~(u{}vTUnsO@ap$w>O6GNyq~OmBQJW|1rc|>L z4j?K7lTe$oL0C+Xk+E*mDE`JLRKe-!VAsMDpS3*F-V@`7MJZnXgU6->;M(fOnnKaK z&gaC>SqKrjLY^2aFSk_BT7wd2a0BU#z#4I4Z*=?NJgI7>t@K7g>POQU8RmPSborcu zW{5U~RI6GZVsmCHu{~DPfVu90?c=zvd>-y`L2w5CnaZC02*g`sIv3yLnzp$_>o&mK zm{^uTIQXkoupDEVU+g1cKpA=C@RjFWG87DYIzheKWb@j&X7u5@YGgpn z;-Z}i{WuaIWqy4q8*r$(=uiFMV_NWt7!YQ+OnR!Dz#ONu=FzFPItx!ns1uN#Xhax` zjR)Cf8@&oC^Rjd|5wOMb^%eQ+h}NQ?Ujhy}34tPh@Etz4DH{r>D@HUH!H6|T=7g16 zIte~^!dU9+Q@*Dv7ajpbk04t@?|hSf9EHE6RR^VFFie- z7+pd~ft&eIzB2CaM2l!@cYLK~Hdm|gWK$bi<>Q^aDvW8MGh$wz7>(gKBpj~~p6F5D zr#`%@HALlA7oK|Y2=A)H-I&ML*tpULLlaZB=G)%RR;a1)eB+0$|86z@QzMOfHei+L z(_Kodgw+|JoYY%MmK_!jLLslFtp}(r>W*9?50NK6$KfMIaz)DABq=KUQ{1o*Ruhoh z5i4(sr6_nhFB1tD9s>s>HVDBqO*ash9RF=_MySqsL-oA9)-I6}^+Gz`e5WjE>mtjE zZb33pL1LfNE0ad&s321{B$4v#qp>efh9LaQOW6)aZ=%>24$jp-J$_Uc*FvESf9L}=&4 zZg1f|EMjZWbjOTF7=8Rm96kKEtg&Tb8e1_Sn%i7i!*`YF8NwA$zKH6KMD6rhxd-t4 zx2IUgEeZ0|7^b{t;e8#|S|`r%9qZ~naQ)aYil6X67t2ydMiYaA;y$>h&2K?KzLaOH zr?xvv6WzZXg`s-VJH$$|T@+9W1SH7`pPT5e^Jm7ip;{iZuED?}lSz5W@%2Bf0{cYn5`+W zJMDD1zYM6btLJk{J_xeE-YA2&JpjIR-v`hWqP8G@{n=F%zFkzr7tFIpU;aqnNTkfY zguU0d;(S8VEokTn79|b=et7A5F8K_s&RQCuFwd`fo6gj}V+~zNG|ZI7`Vsfvk6H!E z7KhT3Xth!5REb@MDAFo5vr_tw?Rx)8hVv1Zs3)pf+{F9>v~PPjFsenSke>9K1{|#!9-XFNSXjz4xqxR-gzq!|^q4>-N@^R?whnmwSWZK(JX? zi{H*CgE09-lZ=iNBc?1CIM$|Ph1j)9y+JK_&QG%-L^KLT=T}giiXWol1@746_=Bm-(r3Ywr;$$5Ao!R z8{HPNKS_@_a9dlnBNnxJP%;Av?blBJKtS>y!UBI4&((i$$SqQEFBSjZz4oOJs)}ahY}v_toE2Z$|6G?~R!0!aGw9(FFT)aPqC1z^(~y z-2XbItpTX~EAJv5y4#++hg2N*hMni8G&3JO#z|@2&9^%P_SY!;*s>7;jX1{APAV=h zPSt!5nkv@pGsp1cOYT=&+@2REt{TU?HXVwCSY2~{+WMK@zvG9AA7#K5K<$k`B}%Gr ziE1Hl2#kmH29H<{%V%Q!sg0>H?tp8T!k1V{qQH=8oB60wXfiqpe`EURrT>C zmlB0`Vxcreo%V5gcyHa;bZq>mcD~{Q&Ds!lF^f(*lTtw1E*xVPOE8k%eq8>dyfX^G zpeDkj-sE4s;*3kvLQ0@<`Lo0BF{=Wh)C(+nQCa|mc5MS2{#(jArtBx4dd6-%L zv6#R*aqXNo7H^GWU*h}OlXp-{?M$&^Tq06>S^ugmE(J4MHvoMblcCzPSy zdoSHK@+c6fq&(D&Cd>UzRr3hSOL=s|@2bRSoGEc{ebney`t06D<>fKWlOJ zf?UulYLoX6Ge~=frGH%!gdH1pkm?YpCL{b(rFBa)GC7t>LvWuq3{%k08Y! zw0~riL|1_!h*w!v6pb9~&EOWb%mSN4B+>$TDTQ#rG6S|qAY|j8k|Bb@MCsg~31@8t z%t(Dy#ig1n5;X4^)^B^ry{vAtcxTr5^c=;|hcTwJua5?MlK}cHVK_6n3JMpYuT8GO zQ&Zk}GQyHNe9=)stsFnaP!1ZM%}YDfI8hc|EDT=$8!>p5kN^bJ z)hoYvMteS93^W?2Unm6~IDnfu%z1c_G?hqIa%M8L+&NMFf0-t=gtg}kRkp*6kn?aP ziJKRWIBA4r{sr`&?^mVvl;fk~lnM}tUVLRHz3nB3WI5rDR;mtMF2PVAXm z=b6m~yJp}1t>p@-pCA)U<_$fY>EIb9725i6gDiDC>+E&scPiQ%FY4m0SmjVXTgB#S zLWENxN*ITk>4ZZdB4x=CftP2L`K&+@y*0ULk`$f>$AyJPGV}eE$6CGO&BKgTU3=#s zot1n$FP9LZ#S_7>vwI_ac6C5aY!Lixpr>Lqi5)_RRU9=O0+7 zkiOH=AQM}J%RVhAH8T!aKq$MQz!eq>u;QDNIbLq6D#jBrl7B!!Av@k<#HK7$^Ijn z{q(X%jU!l~dS3^?--3&GL`^^En0^1zpN8|WYHI?<9X#3)*E5V|W&g7gvCK-9IAHf$)9{4CfjlJDYfC;^LI`5>KkWhS3qLvpCn1f^b$?fbO9{g{^ z^?rx0F9^hMCS!enD*ziIwu3TwrpP*#?4v>omU-ij667Q0)hfGxRo(!Tp*h6`GnhCG zipBrXnOAJ^H`c0vSb@8M%D@S~0@#hm_*Rf^*%(m1h)5-TDZ+hwuZY+Hr@%!gPEbxo z3xb>idn&z;!MD@I3zy)k2(? znH0Z`s3*f1waJ^gp9u%Mk$YS$9ahfzJb5O%_h_;R13;4++vrita!i&l5g4J#v6D6*XqqBX-n3 zd#YT6ymry|)%^C4bAf;>acz@Zh@N5g{R$e9DGIUMF zs*6UGLGJ)TOs~U6h*~qkUCj{)8hVuTH3N@Y$K7$WP4_1rePve7VUwbs-`(HFMoRRa zod1i*)f|v8RBo2A%$$+Tyl{yL%wfNkwpIoFdKWbx@Gw0^3?+^Y8%DE`m+|I%b3RSW zVT>XW$#H>{lWZNP{36EK+I9*C;^OhYo>rdLz!=}fgp|^jmC|fMOL72E+6Tp<9H4Xv`EH>yjHSq3g+LSEm+Q23d9*Xd`D&6D9CPP1RXpW2vRKm7$g`L|iuzv&ucjwYQ8+6749Bwq6zyL_JsVsLyhn_$zv>GDO{}YI z9h)$%e0cRGxl+kzBn6pw9fL-sv@4dHpiRzXwY__r>w28}>>h=3VMk%JgrXDM6=@dJ zf#4B{7fDmZ%!2fWAiM$^E?<<%^Xnw7*^tg*cx+U0xmt(kxNGC>ZU97x^pv=PtJM!j zzyJf4_6Vhuk9m`>msx$zD4!pwqM3?BV|y&(211QZXs?_jj|#uYGO?FPAlTg}A)wMG zF~rguJ_}&hl6$b$qSa5caR~$Ke}TYHT@a{B$JD@NfNAK5Or`DQ`w zMCQ8SGBwj^#0FOK;tk698eBAm%%nBG)DwF;!C%n-_w#U4`Gx#aI#jkaj2M3Sd#{%$ zVI09dAgwgI);UKOt73ffy;`L!=9WWCnt#17xYxz~2NMK`#2O#WVAEs+n<$$wJ>gKa ze-n~s^RRox&)>1=UJo!>p0TY0#9YQYzUh5H9ZLwCnMhBLTp2y|EJU>*<5>KdV}#3s zCs@72@zjL$`V@iFypy!P%YZKu?yqfmkEv-0z*bm5b=%;6o<#rV_o49T?z{FqHJYmF zT%%Zyj-HLJRHk_&i16caGJJ8Zt0e9l zI&Kz?LE`=K_|wvjd+Q{Zv}i6*`egKvobGhq&r}#ZVT(gM-C;x#bq$?--O|Xd^Xc)I zC(VSpvFKTs;#tDHMMyg*kI1>zw)kDhub2-4h8hZrWz@O9(Mh4# zHiO9PPN6=_soJY}lBACYPC8WuG(07UkC-I7R)be^9X04+aY?zkm5)^*V4j5ZX6(zy z*hj&w{Pf$H5a&$WnMJSv4lm6O#nmjZz8P>OFv>5MjPs|b&x;W^&iAh-@U8KV8Tnt) z+_h>1mW%o_bUzL4f@W55GaU8-$U-kb!E=H)!xY-ZV`b34SSBPk?85SV$IijgV5x23 z#Ww{a73hBcZ-4&a2w&`~nX0sha&O0sC?c*WvGWy;48R zQi!LsO~umm-R8!n#fMHBWro#!{v)7vKei}cJA5lJr!fyDozXqoS}olSOr}3T0x%El)!p&3d8@MOQUk_g_?lS?}_*7Ar^)xcmVH&ZKtX zZ3X+EwlXQBL?yiPQLniTh&Xj8a>EH860n38n+}r38@`vHFEER&Svj#EPE3F#JEfr^ zZ#=+q_q{|vO{tY5Byob?;PQsWNaW+uS+w@Cefb|y>(j+IsDx!+=`HFfBhwI@sER|) zbzr;n)S@Vsayfs!doqu{BAK*Jkfl>KJ@gE((=(nMe!sr~zwq@Vai@APKr zaS)m9vW_nwz<#T}|g%VMYMeaOXDBlb_X1S6Av4oXrrpEbPJO z0$*(jz!ZuLbiXVhq0D0$B^wT#hz?Z0&UPSr)*?sSwbZI5(iD9dy=3+dXV1w>C5PsF z;XTfA1~EExAU_LS3bO3iJc(dJtT!v}oQCJWvcZC9s zVfdsEo5442qRqcY{_p@=LRqdcNN%&l%Y18}?iUgzq+I~eavbkwdrKH0ZPcu1kA>k! zLF0eGUeu%FMcDUj4G8qN7l4Vp#s}JexBj1o`zXobFaS2B2Z_Vr+ZKx`AovyT z*$`-3`o?#*l7>3<)O6K4<7O_i2(&m+*X)}>b6=>X*ALqdkGx_bm7I1NWtu5xhaTN> zPpzf&GtlI*e`KKt!l3#b2!v*mVyZ5Z4uir@e{6Wuh+NOtw^}f2-3+ysHT!?05ncG| zkKui8a)(%Ebs;lHkF$2E@Hy!43@X0c>m5=ivR8nXeV6-anV#d>hmh`De^)Y*Gbb2a z=a<@ezutg_tX-crG&7z{Zm5AWTPoDZqs~c8_Xg=6IugXz)@*t9u&JRI}o~R$bPwF#vN>Hg#I&DHkWq z5JP!M8-sx>u@%us?B+-Y@|er!S#xl6(?3xZFjZTaD(s(wa;54(Mc8AK`Hsk2Q{ zV-9P&#l+{NsqnYbyn3n02d=g%$l!hx;#zL3oyyY-PaH{ zwoX9tR@EOI)A`#SHw}y}NMnotInrWS70C;|*ZxQ6#8N(kWJs)0CQzgJc?r})L9BSl zckM>x$Qao_BC)vxq#UH|lY=|%Yd3%lbs6TcY@{x;{SV!6olVdC;MvDGA?3!OHl`H{ zd}?I@>GB(_JoUKNDV%p|E?WH21*TDP5!&AtR%kySOV_G}-=l?7Gq^t!`B#KE}LcHEM zxm_lIo1Y;-44eH(spkyD8DW|mC74) z?2uD*(xSA=!SpQoZgF_%#HVx%W$7vc_U->A9O}af zlG$MyC(BSbn(q$8X64yc6+@s}AjH>+6oM}CiVV#b{Tgx_Pcygn-iVvp{Hi4vUVDW) zfJu!#D7M5?X4t?%asJAF?0%eT{|GsDcHRspy9FJTK6M!5o|MSztW6ur~cB-DZ!Y6(Wif^**pce^Jt67m{48$Pd^({wP^4z(v zDR5XOdzkl7V!z)0{)NixNw1l*obv*g^Kp87P6TMifn#_T43G?GA1@Bl{fv|eGm)}f z?k&b9DMUsw!EF=WeE2mKjICv@MU&LH$DxtLikCYL1HjD$Rd9l)5oZ`Om_YjA;z!ff zwT?}({Ao~XV8?x}EL=D7v)OneZHc-|Mt*$9py!s59S~s-9==mgHHf5Pn3dI{C@EaS z#&aB6%i9zL^0jOCf}Xv{>;Vz~P&7jB@H3=en?_7uqRXEqyEqhMYoE#%p}a@nGy=k& zr0S^!lrOA}w+T);?%uz`?BbAwB!FsxZrZd$&{;z-(aHHd4C4TiaW=2}0h<~Elz^31 zqKdH>mjl+^TGC3uUX8*Hymc}ID62mJ&H8pCWqy2iM0thd{0mc1%V3DnnZXnRINbeL zW$c_KFxF|S4+v9+5xhovie;_E`SF2h${Jgi-osl=^YK1Hf^&ddKOTcfSZV(@eIwz- z23r~IRO>b1{q41{sgt!b`tyr69jW|itBaOOgtJagbV$%~rrD(s z)=~c?h%i!HE=O)J((*SQ%#CRS%xy@viU|quOU{e}*+*B&dqefr?=-2M&gebIvjI8LvpMcw7oT!lST|h_~>g1 z)+fX#to9N&`(L$ST#e1&+$L?*{gnr38#S7?m)#;)*$KG8Ld9{H`@ko@8g407?i#KQ z`nfO=0fSD{vNc^aWl4|3$BC0^bQ129xf7+NcAaHtm|7+g@kqxunVQSdxi=#CFNmxS zeMwFJl)*zxOQs;zC?{gnmFp}G8CH=4&;%?$kJ>2xw~bjk?5#2Ok&rE(X|3$Yh`>|TF=()Zpj=lsIx^y+*?n+VDULG>YU8cuu zKvZ`^YIPVE196bnFoKEL${d&AqiWI!10p(Lq1C% z0=;?`W!uaaPW^{8HPGGT@7obh*1$gR3+f%wd;bjma|{3Aiq3i8!7(1de3vUe0Yl(p z#IZQMmeoHV290INQ(Mg|j-bJEk;=3<9lv?%cJqZ^;e8B`dco45(mErY+IPF{+|XeA zJ9N$~b(tUa;>J0l~0EIHvsJ6&)ub3DRL`icZR&Onx}c~ zP5e!V`#>ys|M>b$j*robPss`RxxkG1UcBPNKoKYqy7{L ze&35Ww&}k9TKtkNQDFw?W>2MPw?=vyJ4arLgR($IG0D0Ue_qye8QPxOBUGO{HJU?^ zcxDj=+-GU|{T&1;$Uws-K*8?xe{4RRl8Z=pl{wXkPZsCk3}^Li?$JjFfgX*r zF4kI8)*(~w-#-j*Zy{HXHI~R7tf`umBuo^*=UC`7Vixw#l`kvETw4xuy^Q(5vbR_m zD84yf4X1}`xjsft{u*yDwrbt4Pi{q1`xP%UJ#zROCLr+UfE^l*rl2)tMm+&reab{Y zL#AQlPKxh9i>S4Ef03%Te>~YX@9CTl=G0&Sf$(I6*y7rH+FL0#L^#nhR}oWYbKT4K!^>SWtR}A|MYTZ=phK^`YYQqU$O&- z+2-;q)OlL;IL_eT+*l6VAv;J-9_G51zN-=EdhhZB&qggs*cW zH`YC>$q^Vc2R7JFWC(_~$+aRIxUdQz8dr>EFQU=)0H5f$$_Sje0)c;^RRd~q=D!(m2);k2KkI&8ygIxny}u|^uG%LUktE*W#=HM z&Y6VPhjd5wZ|u}Sj}9V@ysvSrQ=@PJK*XUYt8@3Dn|!6ER1$@kUH116)C`G0t@O+O zK2)R7qUl7WV&)lz`KJzyzg56HF^K``xe`30#t24sMyht$T9L>qxac&P1tS#BQqx+B zX?0TB?*-ANr|fDal6qalj|M zR>`=hocYUoSoa9K`i<0q&HD4~(_ydJji|WyvUv_iIGDTsa4tFf7Z6T^!d`quzk~+m z{E}Yesh*WVlgg^_%KFV1(|l#CH%ly&?3=KF9cTj|YPyfQ3~Z$-<`uSgcol~?89XP9 zH!zfOJGH*OC=ZyW=O*(2v%nE6a4_WwyE}=iQr)0_iVEa$VN8Set2G>AK|*{$uMjEg zJG2Y+f_4Z~*YDfOwGKx+1LzfZo9#;C!2x0WM<}zM{2HTZfpzfq4V;3~2Hs(tV@RH> zxha9jVV^b}*P4WA{FlRxG>~*W6Nsah%NN@D8Nkz`c_&t;WgwGXlD6t>YXlq7SR7_B z74j^OKfTL3cpe}}jmf*(T{OUuWQSrL{djf`@O&IP`plNv+27L7Ug z-GK{%u|7$?z(R)8+f5l)i{xjvT{3kzC)L9*=`3{Bq2C52>ZIA|0P`S~c)9e^ z$R$#vRlvFCk^WMUviq0C0=@HhG-BfJQZx!sLNM>aajQ;hX+_ScGdS1jtj_o;&bk|s z`-mRfB!_$gWi;nXs~HpEgvtg==>7jGH^Xoe&u* zXgi`*ZOGaxoj^R zW}xjkIV^_L@aE-01Iud`pS{U8O0M0GG7B9KKBP0-zh%P|NWb59FNkp5$ngV75iY!+ zXA}LVH|wE^ICJ2~X027lB7^G@{KIVRp=rC8;K5s|6G892-dfTpKXjJ6_g<9dO@2Q{ z_~u_4@w^?}tm-!hbt?kz+)T00R%q4XUQ^(W1+ z{gF}z$PFBTgR5ud$1NLxqK~N#ne42-cZF(!=VDwgugzJEbk66tS&d5|1(_u zo&IZUZk}@$GT(rov)>^n8PF8_803&nQd$?fHbOLYFH9ln)EsYP+`oCG;R2(D!3Vs> zj9E82jwGY@GTdxc?c+%CLNQp>1r9*!h5~H}C)LV zkcAKx*ROMRxuP}b+KVQ1vLh#)Jv>BOJsk-nlDhdV0q(#k5B|xB)!VgL9tCn5jMB`J;1LIX6o00!{9G%K^5uAy3?2nctiQktd+$ah&pvAF$XLvaBgK6S^ zs<=-mnhB5y9^7vF>UZF(hKd2c75AM&`Y%}D9?>;3nLpo9DFx)B&e`d8eAGc;z&aTI zbx0FWbmj>dRq^}+pkmqBPUNt>0POghfyI|GbNe`o~#Ej5Atn_CD5qPrsz z)*g-Rufac4)fp<34J$O&PAxebk3Wdj2auiN)T+WddZB>hXUF{~8^M_;L8QsGZDJC3 zl-vMoAXtjF7io6_AAItibSWl@D&yEYvQ=whcLbFV6 z46U81!vw5xb_@>kca|a_sFs1GZKd)WDLc5Dz3hS+seJJ({-yB%A`A4;crTRe z7AX6_AbJBUFD?IbePN3n=sD|Ots2(FiH$XP+#UuM5V5pTO!E_8d zObqKFp*^Al7k7duSa%BrbS9rnfsF@5)vaLBcVl^Tu%NY=3TJ9xC^3Pgc@}-sN%3XO zV;W$_!UITnD?C3ECdQE7ZqyOBaG-E@R)U=50mCo(!BFNH?+iWOqd{-}qvStPCnxW| z^7d-=<>G=l?f^=aIVcR~FtZlH73aM_+CEQtz=+%u??zD-=y9sBbP*X~7G&l7OFOT>uH2qlkQPpApnpBFM%E051ptJ7gH>E>^j=F~C1{X|T)E zDD1nU6E+6&{JoA$0?4@*-8B_DKc`?iMU=7X)g{vpUo~o_jN`h12dt#7b|J2$cGLs` zi?>SO@Hf>I#(hdAud1w|(J`c+;^PLVC@dnmK%7RF2_exTW3;%&>B!AqU6xiOJEz~~ z^a3ZsoL3D*!}w3C3-RC%Z~pqVEXsx0(L@6P=z!oOJ9X6i zmya)YQ9VCMEmJWYEKOTUt{guBH)pbP?@C0THkrdAPS@u(X%OO zmAqAGovNhK7z>-$Km%d!WUYHOCkLYLU(km>1qtBfJfdO^<`Y`8V5dK)qtF)CE0KTz cO1bx`eVe`#Kmrxd{_Vg2wt44s(C%X9Ioo|*K;v){xNHi!+M@=T_K1fPPlm6_L-z+ zps&UA9e`*StXR=>R8_cLb&TxMl~ACFs;3~Bt&N%y3o@>xHn znYkXm`|{ea1T}UMzu{5JF$xIU2tKr1ef1@Hm7Y?Po$is*URO<$z)cdHh`-@!{ruY2mGx;9x@DN-Q*bmH2Ic1=8O{lM zg4LMAbmO4K5FKoKtiRk0B7ESfX^MiTWRNP%%5hTC&KW}@?%Y&U$xVf2B>vHjt39VdIT?5j zg8#xfyvwl1h)0s7r<-S-MzBAh>Q8rO?F0yVXV$D*ECialo^|K0f`hLF+fPrH zy(CN8u6^0^m$Y5^jjGcmYbE3WE&o!<=D$ELa2V7?&C2E#RHupR*Bwim;SdXS3VTe3 zxspmYuyjqfu&@kK)o@E{$Vuv9R3mzBNQ$llZJQ23R=~Qng6d*fg^)0O1!~|(l2S_w z9aBHXuGON&M?akqdmR#v$eLB0xg~dSoOILycK>#pLgt7S6mz|q={;Rex7jb0>c zLX|X_n>W48k@H|MK@y4=k({*f@U$^$wrCPP4c>k7U5iDHX?s9WkVsHaE-FfWS;$^` zS~_&!w>nlKVOW^R8wvn`5}ww@6c5e1PrqxvXufR^@FU!p76BI&At9L!g5JCNVlh12 z=6TqfCKDPeF|iao(9`zw^H>1fB|vW%g+s!d{STBJ`lgoR~y{{I`57j1RGGJ z_Y4MRr~3wm;pySVjwNdbf|cUmP7Z|(*UXr`tHrs~!^XNFLpmzw3pOVO0QU2)pj{0o z#WLwaG@rsLQHDiQ-W{E$MY2&2Oyt!M9j~Qzw{Elh!r_}=1D6xjZKSoiI zY?lh{>t)MCQ*5}M>{OtpkKza$hFtv1d9Mhgd^l{g_F1tXtTXm1<_iA%V~d28%l7cb z8&1gZW|lghJxurtwS4MWvGg0PQ*K^)JZVO}C+9OLOgYPC+_{;t{4!1_8LVkDj zrsFW+HxmrJO>`upk4<{U80M{ifp>VW0jSA*~UPGoq;uESTSwUKM@wg72IK_0KfSh6K*(5iB;6h+k>sb+tmUIcFez)oe0=M@=|*^a@`NxfS@(FlaPa02&E;St>Tw zSu3YPw^2df_9#}~k4aGlRi+@OJ;+G54)?BH&6+yeFidcI}qRLFCh|=|I(en0cxC%RibPPK2OWu zh&#Su2Yby9t}7ze_24M-mXl`bA?ICo~k)XIj@NI%r0wL%V?07;<*QQEb53<4Cv zL~DO8UypH40cpQ|uFsPZZtbOApzpQerNWg0cG?XIZ^gcUYN=~$-)X+X?r0pEcRe50 z?aNi!unQl1J89EG)gpmLkJD~RYe}g;NGnc^)IGv$%&ect`S0DXc1vG$?>-zW0)bj4 z7}WCDHxm?q-*TbHP?Ob;UiYDbH`xi((66wuzp_w1KDPpkBLsGT4xWv3MK)R`WL`z| z_sk`9HW7N_mf?U_) zG~~TQ!$m0s2evcXaNY0K8;DVIy>1$yjp$nL1-RqtkPxWwlfZ3$>>hnHftz2uCqND$ zPynjBroBB73W*>qGgV!I-F>FFrrS|UpU1)Y-`M0VBNYuHzoFMK44)0s{((TZsuLBe zE0z(w?l{K!ilJJ-=#8@d1O2YGoilPg`1yr~CdQwHuHT}2nqJT0=vRvVw{pM0dhvmLZW=<1b57O7mq!WK|aiI5~$?Usxq`<6ilc|FEniF3h@R z8%3<)F;~t+Kffky%ZMx`kz5jx6i5)t@B84>KgR1RW|Qv=C3O6_*|ij&2<0^EAb8Si zR^fKcKkMaCuC^yEXuxSvn;!QP_P>eagX);?T|tB%Mse3m{L!_s@isF0bM1fKOQ(PL zHoRR`IJyQ|P;#F$z|4)FYv_3%EOkKnkhf}^6bf}x9N-4x(EDgGx}_$WAzG70VW9dQ zvE-;pl^0w}7ZWf1V)&_sjN`HxecV!-qexPqLC8p%VC~iS5_jvUp=8Y;=Hi|Yq8P<| zGL(T>4u?kYY5%WDe*BLJ>jm~BkDN%({DY-q14U1=~@mK7siMG@&MWHb5bmb zGjEL)OcpWafH#61A1BJOy*rBvDnXyaahvkF;;=TCKN!IxRMYnh$@i4YkuH>VoBk=3 z3nm8Mm~vF|J2Hi{a<}-_x+Yevg)hl$ye&iPfEUSSi9Tl^w=}b;qY`psFGovbdm8jS z*Wn;f!JstsmW!&C)xpleqBkUnHbA46>V58fzxb0CA?nRju9dZ_SD z!RSfOZ^H$`Un~UOwB?{*9cgIK!$L@&+{TjB``sG_G{OV*RRB|O`JS)Dv~LY`$*z*b z)Wf;xj_~aMOonj(&VK{^dUTRhx#|a*CRt$-!g_(B(6ymSEIXlqeB8L9Tt5ImG{$m= zHx0U$$j)IRuL_!L+gf6mI2kciUgcnazhi{=BUq`BD2ihN!9f>`s>S5^VNMj0ivYFQ zNyDF^j|k&~?3OvM9rwTNUC9mB1Ybk>EZpQh?+MD4B&nxvVfPcuUAv_zyaZZtgm(w2J$l-wpg?x{`^q8Woi(p4*zk@yd7mAK&d zH&g+#JUu<11&Q(*&I?7%(Zt&VI#5y4gjFx*-riK2&hMI1N>UaUXg= z;+gfrn(zJlC1s_e-2d1`ga-O9ad1npD*#TAXbFA+@jD+dq8NsN+7{I+Q5C*=MI5Ea z(nI{imtgvFf;dA!uI(mDQa&jmTTFIZ!36%;3)cmXI*Z9g!W8gESp4VYXn3qpAA(`; znlW@J)l(e2R&Vfq{{n?R6e} z=%LXtQn0B;zIy*d%0wtSB1N6reA)6n}K;qPDliy@7N{w5nv_`f*7Z~7wL6`=OSHBd|&%rM+F7?Z3kf~ zbb{{R3>w{~1hRG<(!v?~$_q2vy+v{=sD4MFE=Zn`w)u-Z+g%a!66T?82dVzP|3=QA zU8x2|g9Q1|012dWC)&fQ!fgyYP>NzQynB66TSllUJIiYZkvwJ4kfD@`@v}I|Gylz) zc8s2!#MxbNq7yV1nkk2z37Q)tON!vQtw>aY_oP?`G2Y2(O}de{3*_kK;7T1-7EcDk z&n0x+sxS#w{%EQhbc>(FQD-LMp|I}|bPUYL1HrulmCDADk&>OfAxy2vJ#1`XTUj{4 zdbC<@8e|6EIv!S7$Bm@EMqNSxV!f9i|KxtIO7!Kd}P-Kc^+H0b2hlj|B5c zs#GDjR{Xi&Bo68HgO1>0_fim2AXjKNwdC`h{+azjzr`h!qit0{04_K9p(1C-`Q2H^ zdz+@5D|+M8pf8}PTf>*Mg(`k9$UBx0ji?8lfm1P5g*paHNvTLgFr%M$eJGL}QakCG z23g^II2j%adlC}HXlBvABw1eMiSEUz`F$rm?#Y(6#-Ks3A)uPYeL~d5WLyF2{iLB? z2dYF^r>feuaYz~5zD{5gzTn)>Ib$qx(YJL9lFW8eNpI7mvq!4s?~9^Tf=`ZAVX81y zsY>Jvx$4w=*miK?BJP8;KcQG!h*{ki+nF$i5Vqp z#I#OR1CG0VCjtJcfPu;2vqk&8-|BlN)UL2gFiDc(J^#8O2wWdU93Ir%-SKEJv1e#8 zsg4ZBTvMt2Ddgw%01Ew|BGXoYxDLs%9a&hdUQ9Q|32{)KE_T?Z9u9W6E+E~Zi4{sL zZ!q?$W&EofIcWA=bc8qB{#Kc3D`poKATO&`cRCUP;QkCcdspboAiqmU`Ugte&78Vc zAiW@TGm83BI@tJNfOIOsLg~flz^vUD;lSVewJ_!`Gu^7U)41IA@(ABAvr``=+zYAH30y-;Ej@hP{Cv3o2#=E_V53p|t_~9ivVL}BK&Qv8T_ooeX>Q*AGs1#rM3@ED(hPZO$HTD zf`4vehX7PyXnaEOu2RAvh~&6^ZPQr@IMpa74r;ZH$^jS-RVU0(WKaA0exr2oC_Ibp zSX0OJnNvqgNp%fDfdW)ugLwOs7wS-hs+%3dI7-JnvRQQzuF(6O{CCGH6CA=gBcS+@ zBCn8CTuAK_(9y9NiZvxMPYn5+s*tgc=$kcHn)m7n6Ga}4ryU2SMAcz|PBv)+ASx{D7-@moO=&U^5+yYAEZ8-15?H7!zeYI$b{m2)X@3@*<^F7hq#c z=!XLZ4h3_iil#L=_~;LWrUX^GPE_XswLbN25OR4~M%eLbDSj>^C*jb=76e}2WaB}` zqNPm=$9=SH!rob@PUj?A3H9;AX|v^073<_FlEJv=R`~abZ{0CS=LJ2M-~lA6vmO@` zFbKa+IWevU%XPoEa=2jYg4c<9t+ja{6vm*Gsx6)+u0H9w@k!|GhP0WG1TK-6R>*Mt%VXZWzm?BNAD88d zOwCWo#;n7A*t$-{slG>@zsa3e0XtC2_kMLHgG{(2C{hoT=btb1M>wyyJc?6?s&oF@ z$UIq>@OwWPB#vu)dYuq8Ai+G69=n=!{zIOV1BKU=|LO7pZZHm79+)rs1v@EC!goEf zjJrG5m^Tgle`<)<_ul_+2q@450{E}4Kx9t^0MyL^vO#D7pa4*QMT$7F!UE#_{6uR6Q%Uq#X^~wG{uVx_B z&FM*PG13Bm-)=Rq#;1v}j^GYx;VtY_3|t0^-c0_az65TqUZvJSJ;`5Xo9X!xP6G#k zPtP0QTW``olR&7K-97bZAW*IjSkYVkL+8`|F8{sw8G6Ne8Cu^P^9}VXe+_K$-32~- z!JY|J?Ot`odl!8ny~uAOP0M!*Ec-Uyw;u1l_2l)&0$G4U-=4384@OC;|A0+mfz%%}%A5EayKIV(>y?#?JKwz}D)VJhk|DFCm z`DgY$_aV2rX9Fk>#01)Xt@gP-055?L0)xKkuZz#)pYFiGti|u+ZO^I{NID3&0pU-u`ujL8Krntkre1d`0j5(k z0moZAw5=JYpu`0^8Ify#tnYX{x)~KStlf^yG7d_zh;@&25D>vGE*)cC)q%UFlLly+ zV>0k`zBszodPSISM)&abNj+d8DWqoTi#dPdGl|E^Xw{8L(ll8d*+j4$Hotd-{y_xa znJ`8jhwri}D^`>)sv!2Qb%Dz-vt9-$9=MaJ!Q^ah9YnkQ8-~=~7uye##u*<-ES=+fWE67lV7r91kNmBdJyiCHVC1 zvL|gs;y8(j$Uf8hGF=T8q_q06DnB+qs-?=@QV`1ql9m?)jV@;tOzYam-{(W~u+3R( zPa1(N&N=?L_mr~?<2$6)Yz+x=YEstz-+^Qqo6MO0kze$Wx&;vUc&(S5r~fK(N~2$6 z3eRuz4Pq4)UGH;nEvZ#THX@W0K9E$FJ4ct^rF~nTn##)rAs&}AMSokb?&K=E7#6jZ z${i=L8u!fLym;~-?6j z2nWXVt|WzpAKejxzj&>3EGd=$M@eZX>*hdl#P&(uR8c^2zGg=HU{@3oSmQuvT6%_C zmBEa-kv0^RWa>fP0O1|H#*u}UB(T_p1|Hhk{-bgnfplOCUWm%i8Ci-5?@|%PTA3Z1 zq2z5W-}t40%(gSzh+-(~1@BsEs8t|}aS`}k&fS1n*nc^KBOhbt>tFv~>M!$OB9;Gp zx14&>{+p&7xv?UCb0yMP3r#uW=PtlHlj3Xn~G$s zdUg!<8u>IA%kS9>UVZ~>60fh-B+#Fq=t2`dwy37w0#vQ{ME;IN5okT<;XTd^g{tU5 z&-t&9zg#X~^n;lpB509v6zV3Gy?K1wx>%}e#_H{=onS_>Be@i}5aeqW9CVvXG|=RoLKjpfc9SD$UKX;FbhrJe7{9^-a$*k1Kb zxQ6(dY{-^)e>8kRU3Y7Xn&ikZxo_B<+7OB9gg0keQvJL-24DxD=D}&E=P|V1%rfZW z*uT)gB2G^{wmDS>2_9J_!F93lGw^(fnQ|P;gUy|k#xae4-|J6J&Rx2kUxWkIGB=6Y z!N+=wsO@57@P#Ro?XgAxM&{|k@|0Q`I<=8#+4pI7~e)L%hI zWOSynw|XPW&BgL@Nh>6dy~6E(4SLfftsE!NRJ>b7&oo2hSUdX;taSAzBcaT3vES(%6wj%(El;9ZJU9&^{h-#lwj*3cS+lPfIM(?M!$Ts5cXc6$ zdHZW;zRtm#c+Lk%@cFXOfIa0v+E$IJ!Wwo9inM6MaTm4G0E@Jevq34i0xc|NuXhX+ z>xnrM-k6ILO$4mBs!6O)y|ArGufd{&tfn*TH z*Id+_0`nvfGX2hqbiM^&O9m2;d!bi_trIRHr7BF87+8en=v7#Gt2*XW~)c6*=5KBQ?0~sREz=RcfW_MW1*G_VQLAZ}f9|UUB$7j7U-x z85_a1dASN1?3u@JtAQ)aOTy9oZpURQ9d|;WOXTVy2Z<|E4iSArGqOFZ02prQJ|rAv zF*~2G6M^xOA2*MT`FwngEoQ9UffO%JV@gEGLvU0`b2HmHlPA25Wq;B zwo{I!Kp`iB9`;xL#c5pLA?M9ou~n{+Sdit!%DYJ4ZfUR%#6@YuZ+x{Y=p=~cDa0=9OSLpYYFQ{63#po>p=uuLGiS(5!?p?S|@&JBt+2F?f~ z$AQ^r&_d8$TY#XDd1m&vsYebXzZmEO=Z&M^r|N2}rijafosKTN`Z!EvuK05|cf@B! zjOgu5V=nAquEphlpQ(e4OO5IdSa9ggn-Q)0s{UW4VAUUs&o7ef2aC-?=aJH3E91hW znC8EisLLt}msn$1#bS(rTFb*z169%+FvY)-q8+G&T#7cV;(o=Il#QcURnqjFxUJ31 zBe1*V2lBTHMd;P_O+a|k|Fk(LYCpFSbgUzEyI$GvY=QI=XAtACoYO$6zJ05pVTK%t zVrcGd*Wgi}EJq6ch^AJajpVlAXJvGq-V&7|D-{iHqX~s~OpCXIn<1Z@$$g5ykf^6= zzC{*A_=6jxx40Uy%@Vm@xjZ^`Ua{VTB{BQ1AIfZ*UL>4vB*Lm?B8F1?&Xo3tMKH$3 zi3ENtbl(#Trs`mNrl2jIgX^;VC_6rnjLXw<0^oS^(j$*IqARa@$;)qh>!8S$5 z_@VCc6y)-#W$Ap&5_uxcn4A(n1h1N_!yoijEPNm{&e1icq%tH)l~y*v(y_hNlm_?< zav)g4OM7wbb;Vfb1v!^jJus1%+Hp|e95TP*2fTe_a3}>-_TmC;OVPa0b08VRKT?r6 z?HryDk>q8df=t&UlZd2}*Yn*s>f!}&D{DH8uzYq6sVFbOdq{HoGd_Fip9v0tBXD!^ zpzNXiF^-DZX^EkhDRK~b4kLbXYUxT|6X6Hq`6xse5muT*Wz(BvTL)17gm~+0L9ipT z&>aOY3_W2UYU8~ltf!;W&ngw$PWMt_+TPMqQ4LH*iL1Iej?VlD8tA7zc-o2DeTG2b z>M#be#1m@Be9|{7dY%QJ*0lW_VLkb0)v3!)@`#)e-{#lpjpXClq?!2$BcfULl!WpE zasoEzYx|@zy{xk#Ls{Z@yu&Sigg~>ZZf-&J&nnBXG9sCO7lmqQbP7YH{L!DV6%z zz97@c_O_WhK}^42Sy!TLm&H<|2{=1`Pf7AHTolB47gmJfqjaPv6qFpNrg1%l-!m6) z&Evxg)=bYMwl=U2MCTg?Hq$`#IV36FlV<&!vdJXQW7QN$CIRW{XC0}6*ny+W$=i{Y z5k~w)wLX(7v*;eUYE)o7O!nPV<0}aREqeU8U-KlfRE$WA{NBg1 zg3cFe9a1?}j3F|r*J&NPQ@J7tbT(AZ!EYj65;PFNf`YekA-!i($wD`+7!WX){rP=W zn7<_~P)3u=C1}zp`i6`^-{qHODPFca(e0?)@fJ*Tm7*J($gEz{Ef&vo=b%SPb7l}c z-jya;g5sxIa}+!X*k!Xnbok`1QHcV1 zdI4dxaq8P*KCsUc17No^$y;+AHMuH7fE~2oW2g{8ZRN7W1=q_M$_wc7%a!Mv6pY?g zO5Nl3E2M+a{f#CEw;ohCm7x4Fasg}1VcXoAAd*N|vYS|n$TLT7o!j=UIklHX6D;ro zXH}g`)!-WV1r?QEsLh$5XmNH1m5W4%C+{W4lw zrnUix3PscqLge0U6rX7|LGO3*$e1p#rQo+)!dL{ecwo<96nA;5D&1K_fh^@=K}-Dx zb;=IfK$Q0DU1gA}BRP45#3Mhc=JSLV+;mFaq5o(?nmS?acnmlb;XGESANdf{Rg(cgiz zwM-CI>Vx#JEr{Uw_P^Y&>xO`u+(ucl7~HJ`i6tjD!%oxsX_1%quHRR~@g+&FT@dyB zeRX^dLmAqaMmqmSi)5dAv-$e-%EMbVR^(N|bLC+5##;SV-inn?ETH~Gq5El@Tr~3v zcc5@Kxj->edV*!iG;`)cNT$j#20iY;U3ubQ%D;a(-X7*BqL-4lylserIPIuL&CEnxi zN((GmX9mZiEP=BsvXnN?tNY@_$~IaFj&f|Sm(6THMNOXU-e$o1uOy#y7)K0sg}1bXA^X2>g19mAJS~h(zLcgiaC}L7 zXW`S7uJghbIztL7pUS&sY4@;aNk zs_XwuH%}M+!78^hw|TrN>@LJPl$bTZy;%A8vWH^p_BjVBg0*Liavtw-^oTXackVlfE33wiJ@5QZAD@qJJTMd(6>eStMo0An zW=j>D62` z`t@4?TozIrVmlk#Hn5iPyL!w{9>L!5P{=E#pVR`x7B=qA6xK*ekIAel!O_sT=%)6~ z769+E6NcP%vhtAjvDI|OdDyXsHw>3%d(?MvGaeXJp9OyK!&4^jPH`E-gAIgHZO?ovm*hT(fn zReW>M(3W|As(W5y^SRbNb2h-;Lh}V_bvGN9lHE?Q_=Dgz;3^Efw#vq$*2- z%Mlk~+}lVkNrsAxv+=TQxLdcHeI}|RkMhHzj5&Z|=;SJzd$=96YRIDV+Yt^d3MXv% z%SGN+V+AvdI7g`r#YQ1-`4@&5(*h}M`_hK|5o&}>W?4cfi2=>yEgrqq{#i5iSL#8+ z-Jz2b+rHU`M1cB%vzm)g6*xG}o+hwPeE*l6qn z$f5fV!fksaeX3avTcg+CV9jol?k}MD1$kwEaCTP!PhOOSY9pD3T~yz8ChG{CH!esT zA_1->9{+a#w8cansG{pLuf1pR#T~;POw3tGUj`fbp}3)a^(MC8m=PQOK_z-Iskq?# zH5WmEdIIvs4w8c~ANRNeBckrTwd$r{>h&)K=3Us-&NAgXFf1r_oo1|ZhSJ8%i;-4@ z(;@*kD{!2}VAM{Y+}?b2Axl;$#p^z)Uhf=~RyB-j=bj342ippxAMwfL?fXQEv9c`z zHn1t2&m9XCb`hlzEqbh)t~L+O+&G$PDny>sg7K>b0_}>>-*{sil?l~Kspy?U2kAr_ zolUejrAAaUW?I0IPOG`S5vwEY;9Yph?5QDvV!QD#5Y?dhHe6!uNVI~bwk&2S+5g{m zD(j3G69GhjUaNDTqZc|ysnDB>pK3obE9{>+LG;=tVC%y62KMXSW_m8ggliZ3eHc#m z#$L*~s%nuK(FRYn{$up}jDbDUuzl9X0qNdAfocimMGi@(bFObC;&lwGD01Sy_jnV_ z9@d<8nnm;mKOFkz0susg@9zE+yWOet!#jA%$iOUd1@^^00f)Q7=cF7 zp`nkCLN>5lY55{n%JfUc*spTez$(%ac6L;+&_%#=&WP4xZqaxITcH@?g_zuYe|0`= zgIl%1ip3cS$3AuJ&;i!v#j-YBauqs-8U?jwdRs%J;KwpAjpKH#p_H7b?6ay;I);K> z_bZMu!u(i~@JQ-+3*u3fZk!$>aNwQgZ*&dz?_E(`A&C->jYB64Q9;(P#-dcfrdqO$ zsZjo<<0^mKV<;S6&7;Cb%@hV^_n^}5j;wkm8hm=66J=s9pIXm#xiCGh4pR@&cj9O2DhhJ@|>lm|%^_CvH5}_bQ^qnKr zHo2=7@cGY*y5(s(k=8VZ2O}2^^h#kHjQ6jcUhK zDImprsF9#b%q43n<;F(K&Su++0{5)oafRjJE{w^_dzjBG?NlkK@Bm`e)-h{gg?JyQ za+8TALC-F)(l^JCIthHzgNip$@<21dt4`k~v zfI$oP!WR1-=>QGmKXM^(9f~>xgRsqMSl-4u^&PPn4HMmRkz>|>xc9Kz0reY_5fUJ< zAbpftnnw_qgP8KSHqpUrx9Br{6LTHt?X5mEa<$swqewF}Zh@ClN(w%{wHw{+i!B0Qf>bK;_gpONsFB7M3)N|p20^Fv9vbhBE zy&o{lb=Ek~Z%lESzeQK8ZOTKKZgB_XE=I%F^Hl#cow#3pKZt+6Wg|XUp!Dso8 z9Jp4$RidTp@Ux?_yiRUE*Bh0&uI`|*P3((nskVb<+j9JV!9?sb&d!HUHLVxD>J%Om zvc?|7w`dnyVDVYQC(W2XRBS8emq4a=F(2B0LrMQx!1pzE_7oWHr$MuC3+zLqoZsL+ zW8koW{UwBKK|rEn9g7G@j>iyuF7?K!Np@GE(1$qEw8ZGhdti8Lu_gL9uylCjWI`#t zbp)|UVkb<`9Z!}6O!0B{64o@{Q~vM|qjS8QBY{+x=#Sw(KG{i8#ZBpuJ-l~AV}hNO zrQ}AiLnL|}i~uDn*OzIW>4(26;=EH|3zQb}b38(~i+R7vt<@(61W5hmgqUnMrA(&E}s!sS$!lunO6$Yb`MMKt1#4(@@Zc(L{?I-y}+*Kd=fX=InUn zs*O!(K|y{UZ-@V5*_Q+@__M`bY*lqVm0Y2Z4v(m4U^W=<$e*;Q+U!0*#1S+Ab1bg( z$Tk?^{Rk{#Xi9U}bg(j8&p8)xiO!aeN@x{+fPzf&#gcpf??TSft-iC(G?-JX4#G6+ zZ#~`imD%0tHkHh-FJ=-GC*fCA2E?1qz1+AS@BW@H$}D(Xz+ZF)hsV(YCg7zPu`(wj z=FI)p`Gh`6&7h6s-dy=b;eL)I;*^&#^ucTMmDn$J*oWbJlnr7#Bp*8?!s7?lUqp{` z9NLh~!C0U9Wt_1hF9}vpPjl_YWe5mCg(zd>0)NuRzooS9BJJrhd?0KW2XTdVP&l&ZQAMtmepto1c(uQsXQ4ANstuZ z=LXO&@E#%b3%k4vC<495a-($&50d+6s&vtWURo5f@zszTJi=k?`_ivNU^T^m8*nJvURxReKP5Fxv1KB`IlmDJj8E zUDK_i`3L938R9BK58dtMPvsvQdvRCgUzf`Rsy!oyNQ3qUuL7VSPGE??TJk!_pmz+` zebFT>+N(QDNniIiikj_rvWa|0-k;F{Y&$D*Mxbva&Oz?GH|sLPY>5bU>CqP`zUX2s zk@q8CCYhP`4!Er7Q0r-`x4%Gih)zz=68w}Ct2BSGC+)@Y6nrN|5AqPoxba697Uby% zRV-!tIff(YbsJGxK6!u$VEtUgnVq&tC(B@)qnszbf1;j7CMekq)h-r_Z1wzZe$R|+ zN}2sRr&2=)|MqhWiU%fu^yoifpO8tIanFwbF@G@P@*4w--j9ICya0RD@8B6K%VfNR zpxA|4tY3*{%0sHBmrpvWC@rO5onHNGqYT4N&2GUd3p``2bx{-OH|H;C=z1T1{#QsD ztwdAr;AhmvL9^tNiy^K_YaG)3sW-bA@U~cr&m%b;Dm_YvZ6JwN+W{erTP~2U?;;IwOVG z{i|h4r~9byKHL5}nq~6W9Zbg7RANNKH^}afsyJ8NVS@HVsn@hoU+<46mw*~ZI0Ad> zyn0?U(NR`7Q@>Bl@gy!5JtZEP_w z){WYWQd=q35UQnoxsTF{MvnVL%0OcbLXOH2i8|KJ;Vf5HqJN8$1)jA;iN?&iPeSQ( zNU(BcbJuA3(>{j)zmNlcMAv#MSN<|4)!nsGG)L~-2IXiHe_=$8qupo3kbJSCiWWn& zZy;MWbKGHD!ggE6k(Wm-V^H-r&V!oT{?=g`0W}Tgd-UGu*|1x;%ya1o7Z*t;ulaO$ zWZ1SWha8@;0BHqH=>Lan{i6qlAzA!}OwJH1h;3lc_;(Lw0`;+=DZO_<7(4VWAXeWk zgJmd3Mc}mc$bJtli%Pb}_Ft({>;^Qr(j`$6221$!@a_$todpGGh11kSH^YRhXOZcZ z=TH@oOvJD#?F|mmm1=A!(vt>BThqh15g!?BK=!IX5}T?!P%@djvdH%8{@2Wp!X23$ zdV@CJZTm_no2(mBAcfhJ&^&KiE_Y3L>+rJ#^}YTJQFW#K&$fQPmp7ZNNh#dxT?J`( z&kc3Gv%ioc;sR}Q(0fd&zG^W-L@t`0_3@C_f@zndmh`~K?q@V#R&brNn*LWvzzNsk z$F0F@rj7dE^INZ%+(=2m&aMpW7n!}vo;5>P@*jt*n6tA^yl%Zrxp*2v`z;Wz>{ITD zPT!5T(l8P}^qC=H&G+^5CIHdDv5=s50*7yqr&wQng&@&aT6mKyDC9$#B6lOc!1D7~r7R--u)>~WHauiHPNwASXrvsY9Mtm9RWbI}t;9Lv>CtJ(oNBNbcrRTZl9gzsKsppxvRE8VBt2uQqhSuiP+ zQ@O)IJ{Nc*G#ctr5ZKA?AJk-{O9K<3LPf2_BHd@H#tVWlmF=3t=?Qw0{!&S}TRBbl z) z0`T+Yj2LI$z}II|{BY0bk1F6VYzIaW-4s9<90U5H805I`z=;cojU--7ESg8^vAp|% zn4Sy^T4mqppGPS-{mf_D16$ePz;ce$@Rs`Q9?EpwbJc zMXi?P{!v$%X^e37*J9a|-_{@F(_7iG<$e|;*hhh~Oz5{5u}TKmj(bsW-rKE&mB04x z5KIzJ?a#<0BM*IBy^=$#)-hheE7c*xAg`}&_9$OEm7m$IB6ao#&g3U!(H;_K{TI7wfSDbs1L7f45D5#Ln?Eacd=+iII6Mh5sf>%aUvz*pKAtA1W<;OEgFW7XGj&e6uU5qf+xfv6TTz$GTMLMEM{lgqj^2BM^ zPM>F2!^b>bX2AMb(N1Y>EDs^$4oR<*j3Bl)u zuRCWqbD*)LO^mtIrziWo9Oc!$>xvgm>)J;U(_7`a`TNcTE6^l6SAl+!HDyQR>M87M8VIUQqrgry2}OX0Q)JcP`01Wqd-2-&4M%fBI@$r zGJmkvrdo8;Mt5AQSnAq`FHQVgK*SG>{m0_L{K=BREWXIqq6$5^@w8mO?N1@8;_C-) ze%GKXrVcwNk|%p63z3Xqt1%(9v{j|0uImv$>_&w>w^3$+)9}rglbvIJ5FYY22=B$f T6L)wnFsk{&`2T2w|9AXf^%+!M literal 0 HcmV?d00001 diff --git a/Stick/Hello_13.webp b/Stick/Hello_13.webp new file mode 100644 index 0000000000000000000000000000000000000000..2acb03cdd6e92bd9e1dd12a95176b4f112776b41 GIT binary patch literal 23666 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 UPaij+8Nc5~pLzfQ0000004f&O*8l(j literal 0 HcmV?d00001 diff --git a/Stick/Universal_11.webp b/Stick/Universal_11.webp new file mode 100644 index 0000000000000000000000000000000000000000..dfd373b6756a82ccef8e1353e84a81a90e847553 GIT binary patch literal 32894 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 literal 0 HcmV?d00001 diff --git a/helper_bot/__init__.py b/helper_bot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/helper_bot/filters/main.py b/helper_bot/filters/main.py new file mode 100644 index 0000000..021e287 --- /dev/null +++ b/helper_bot/filters/main.py @@ -0,0 +1,15 @@ +from typing import Union + +from aiogram.filters import BaseFilter +from aiogram.types import Message + + +class ChatTypeFilter(BaseFilter): # [1] + def __init__(self, chat_type: Union[str, list]): # [2] + self.chat_type = chat_type + + async def __call__(self, message: Message) -> bool: # [3] + if isinstance(self.chat_type, str): + return message.chat.type == self.chat_type + else: + return message.chat.type in self.chat_type diff --git a/helper_bot/handlers/admin/__init__.py b/helper_bot/handlers/admin/__init__.py new file mode 100644 index 0000000..a93c964 --- /dev/null +++ b/helper_bot/handlers/admin/__init__.py @@ -0,0 +1 @@ +from .main import admin_router \ No newline at end of file diff --git a/helper_bot/handlers/admin/__pycache__/__init__.cpython-312.pyc b/helper_bot/handlers/admin/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ab05eda4048a9895f89bc09bc718bfc9039fea5 GIT binary patch literal 217 zcmX@j%ge<81opotrO5&5#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r6;%!kUb? zcoI``GxOq$@=Hrni~Ka1Zn5MhX66+!17%k-dlIY~;;_lh fPbtkwwJYKP8V+(rF^KVjnURt49)nyF8;}D4#~3>n literal 0 HcmV?d00001 diff --git a/helper_bot/handlers/admin/__pycache__/main.cpython-312.pyc b/helper_bot/handlers/admin/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9ab95a0f82e3c06ed4237824e134db711820077 GIT binary patch literal 11097 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 literal 0 HcmV?d00001 diff --git a/helper_bot/handlers/callback/main.py b/helper_bot/handlers/callback/main.py new file mode 100644 index 0000000..69620db --- /dev/null +++ b/helper_bot/handlers/callback/main.py @@ -0,0 +1,164 @@ +import traceback + +from aiogram import Router, F, types +from aiogram.fsm.context import FSMContext +from aiogram.types import CallbackQuery + +from database.db import BotDB +from helper_bot.keyboards.main import create_keyboard_with_pagination, get_reply_keyboard_admin, \ + create_keyboard_for_ban_reason +from helper_bot.utils.base_dependency_factory import BaseDependencyFactory +from helper_bot.utils.helper_func import send_text_message, send_photo_message, get_banned_users_list, \ + get_banned_users_buttons, delete_user_blacklist, get_help_message_id, send_media_group_message +from logs.custom_logger import Logger + +callback_router = Router() + +#Инициализируем логгер +callback_logger = Logger(name='callback_logger') +logger = callback_logger.get_logger() + +bdf = BaseDependencyFactory() +GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts'] +GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message'] +MAIN_PUBLIC = bdf.settings['Telegram']['main_public'] +GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs'] +IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs'] +PREVIEW_LINK = bdf.settings['Telegram']['preview_link'] +LOGS = bdf.settings['Settings']['logs'] +TEST = bdf.settings['Settings']['test'] + +BotDB = BotDB('database/tg-bot-database') + + +@callback_router.callback_query( + F.data == "publish" +) +async def post_for_group(call: CallbackQuery, state: FSMContext): + logger.info( + f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})') + if call.data == 'publish' and call.message.content_type == 'text' and call.message.text != "^": + try: + await send_text_message(MAIN_PUBLIC, call.message, call.message.text) + await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id) + logger.info(f'Текст сообщения опубликован в канале {MAIN_PUBLIC}.') + await call.answer(text='Выложено!', show_alert=True, cache_time=3) + except Exception as e: + await call.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + logger.error(f'Ошибка при публикации текста в канал {MAIN_PUBLIC}: {str(e)}') + await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3) + elif call.data == 'publish' and call.message.content_type == 'photo': + try: + print(f'CALLMESSAGE - {call.message.text}') + await send_photo_message(MAIN_PUBLIC, call.message, call.message.photo[-1].file_id, call.message.caption) + await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id) + logger.info(f'Пост с фото опубликован в канале {MAIN_PUBLIC}.') + await call.answer(text='Выложено!', show_alert=True, cache_time=3) + except Exception as e: + await call.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + logger.error(f'Ошибка при публикации фотографии в канал {MAIN_PUBLIC}: {str(e)}') + await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3) + elif call.data == 'publish' and call.message.text == "^": + user_data = await state.get_data() + media_group_message_id = get_help_message_id(call.message.message_id, user_data) + await call.bot.copy_message(chat_id=MAIN_PUBLIC, from_chat_id=GROUP_FOR_POST,message_id=media_group_message_id, reply_markup=None) + #await call.bot.copy_messages(chat_id=MAIN_PUBLIC, from_chat_id=GROUP_FOR_POST, message_ids=[media_group_message_id, media_group_message_id-1]) + await call.bot.delete_message(chat_id=MAIN_PUBLIC, message_id=media_group_message_id) + print(user_data['media_group_message_id']) + print(user_data['help_message_id']) + await call.answer(text='Выложено!', show_alert=True, cache_time=3) + +@callback_router.callback_query( + F.data == "decline" +) +async def decline_post_for_group(call: CallbackQuery, state: FSMContext): + logger.info( + f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})') + try: + if call.message.content_type == 'text' and call.message.text != "^": + await call.bot.delete_message(chat_id=GROUP_FOR_POST, message_id=call.message.message_id) + logger.info( + f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).') + await call.answer(text='Отклонено!', show_alert=True, cache_time=3) + if call.message.text == '^': + user_data = await state.get_data() + media_group_message_id = get_help_message_id(call.message.message_id, user_data) + await call.bot.delete_message(chat_id=MAIN_PUBLIC, message_id=media_group_message_id) + except Exception as e: + await call.bot.send_message(IMPORTANT_LOGS, + f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + logger.error(f'Ошибка при удалении сообщения в группе {GROUP_FOR_POST}: {str(e)}') + await call.answer(text='Что-то пошло не так!', show_alert=True, cache_time=3) + + +@callback_router.callback_query( + F.data.contains('ban') +) +async def process_ban_user(call: CallbackQuery, state: FSMContext): + user_id = call.data[4:] + logger.info( + f"Вызов функции process_ban_user. Данные callback: {call.data} пользователь: {user_id}") + user_name = BotDB.get_username(user_id=user_id) + if user_name: + await state.update_data(user_id=user_id, user_name=user_name, message_for_user=None, + date_to_unban=None) + markup = create_keyboard_for_ban_reason() + await call.message.answer( + text=f"Выбран пользователь: {user_id}. Выбери причину бана из списка или напиши ее в чат", + reply_markup=markup) + await state.set_state('BAN_2') + else: + markup = get_reply_keyboard_admin() + await call.message.answer(text='Пользователь с таким ID не найден в базе', markup=markup) + await state.set_state('ADMIN') + + +@callback_router.callback_query( + F.data.contains('unlock') +) +async def process_unlock_user(call: CallbackQuery): + user_id = call.data[7:] + delete_user_blacklist(user_id) + logger.info(f"Разблокирован пользователь с ID: {user_id}") + username = BotDB.get_username(user_id) + await call.answer(f'Пользователь разблокирован {username}', show_alert=True) + + +@callback_router.callback_query( + F.data == 'return' +) +async def return_to_main_menu(call: CallbackQuery): + await call.message.delete() + logger.info(f"Запуск админ панели для пользователя: {call.message.from_user.id}") + markup = get_reply_keyboard_admin() + await call.message.answer("Добро пожаловать в админку. Выбери что хочешь:", + reply_markup=markup) + + +@callback_router.callback_query( + F.data.contains('page') +) +async def change_page(call: CallbackQuery): + page_number = int(call.data[5:]) + logger.info(f"Переход на страницу {page_number}") + if call.message.text == 'Список пользователей которые последними обращались к боту': + list_users = BotDB.get_last_users_from_db() + #TODO: Здесь где-то надо добавить обработку ошибки IndexError: list index out of range + keyboard = create_keyboard_with_pagination(int(page_number), len(list_users), list_users, + 'ban') + + await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id, + reply_markup=keyboard) + else: + #Готовим сообщения + message_user = get_banned_users_list(int(page_number) * 7 - 7) + await call.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, + text=message_user) + + #Готовим клавиатуру + buttons = get_banned_users_buttons() + keyboard = create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unlock') + await call.bot.edit_message_reply_markup(chat_id=call.message.chat.id, message_id=call.message.message_id, + reply_markup=keyboard) diff --git a/helper_bot/handlers/group/__init__.py b/helper_bot/handlers/group/__init__.py new file mode 100644 index 0000000..7ef7e16 --- /dev/null +++ b/helper_bot/handlers/group/__init__.py @@ -0,0 +1 @@ +from .main import group_router diff --git a/helper_bot/handlers/group/__pycache__/__init__.cpython-312.pyc b/helper_bot/handlers/group/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..995ec3a8369b0fdbf3b61746ba0ff73b7c901217 GIT binary patch literal 217 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 literal 0 HcmV?d00001 diff --git a/helper_bot/handlers/private/main.py b/helper_bot/handlers/private/main.py new file mode 100644 index 0000000..46ff613 --- /dev/null +++ b/helper_bot/handlers/private/main.py @@ -0,0 +1,281 @@ +import random +import traceback +from datetime import datetime +from pathlib import Path +from time import sleep + +from aiogram import types, Router, F +from aiogram.filters import Command, StateFilter +from aiogram.fsm.context import FSMContext +from aiogram.types import FSInputFile + +from helper_bot.filters.main import ChatTypeFilter +from helper_bot.keyboards import get_reply_keyboard, get_reply_keyboard_for_post +from helper_bot.keyboards.main import get_reply_keyboard_leave_chat +from helper_bot.middlewares.text_middleware import AlbumMiddleware +from helper_bot.utils import messages +from helper_bot.utils.base_dependency_factory import BaseDependencyFactory +from helper_bot.utils.helper_func import get_first_name, get_text_message, send_text_message, send_photo_message, \ + process_photo_album, send_media_group_message +from logs.custom_logger import Logger + +from database.db import BotDB + +private_router = Router() + +private_router.message.middleware(AlbumMiddleware()) + +#Инициализируем логгер +private_logger = Logger(name='private_handler') +logger = private_logger.get_logger() + +bdf = BaseDependencyFactory() +GROUP_FOR_POST = bdf.settings['Telegram']['group_for_posts'] +GROUP_FOR_MESSAGE = bdf.settings['Telegram']['group_for_message'] +MAIN_PUBLIC = bdf.settings['Telegram']['main_public'] +GROUP_FOR_LOGS = bdf.settings['Telegram']['group_for_logs'] +IMPORTANT_LOGS = bdf.settings['Telegram']['important_logs'] +PREVIEW_LINK = bdf.settings['Telegram']['preview_link'] +LOGS = bdf.settings['Settings']['logs'] +TEST = bdf.settings['Settings']['test'] + +BotDB = BotDB('database/tg-bot-database') + + +@private_router.message( + ChatTypeFilter(chat_type=["private"]), + Command("start") +) +@private_router.message( + ChatTypeFilter(chat_type=["private"]), + F.text == 'Вернуться в бота' +) +async def handle_start_message(message: types.Message, state: FSMContext): + try: + await message.forward(chat_id=GROUP_FOR_LOGS) + await state.set_state("START") + logger.info( + f"Формирование приветственного сообщения для пользователя. Сообщение: {message.text} " + f"Имя автора сообщения: {message.from_user.full_name})") + name_stick_hello = list(Path('Stick').rglob('Hello_*')) + random_stick_hello = random.choice(name_stick_hello) + random_stick_hello = FSInputFile(path=random_stick_hello) + logger.info(f"Стикер успешно получен из БД") + await message.answer_sticker(random_stick_hello) + sleep(0.3) + except Exception as e: + logger.error(f"Произошла ошибка handle_start_message. Ошибка:{str(e)}") + await message.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + try: + current_state = await state.get_state() + logger.info( + f"Получение данных для приветственного сообщения пользователю. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} State - {current_state}") + user_id = message.from_user.id + first_name = message.from_user.first_name + full_name = message.from_user.full_name + is_bot = message.from_user.is_bot + username = message.from_user.username + language_code = message.from_user.language_code + current_date = datetime.now() + date = current_date.strftime("%Y-%m-%d %H:%M:%S") + if not BotDB.user_exists(user_id): + BotDB.add_new_user_in_db(user_id, first_name, full_name, username, is_bot, language_code, date, + date) + BotDB.update_date_for_user(date, user_id) + markup = get_reply_keyboard(BotDB, message.from_user.id) + hello_message = messages.get_message(get_first_name(message), 'HELLO_MESSAGE') + await message.answer(hello_message, reply_markup=markup) + except Exception as e: + logger.error( + f"Произошла ошибка при отправке приветственного сообщения для пользователя {message.from_user.id} Имя: {message.from_user.full_name}. Ошибка: {str(e)}") + await message.bot.send_message(IMPORTANT_LOGS, + f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + + +@private_router.message( + StateFilter("START"), + ChatTypeFilter(chat_type=["private"]), + F.text == '📢Предложить свой пост' +) +async def suggest_post(message: types.Message, state: FSMContext): + try: + await message.forward(chat_id=GROUP_FOR_LOGS) + await state.set_state("SUGGEST") + current_state = await state.get_state() + logger.info( + f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id}. State - {current_state}") + markup = types.ReplyKeyboardRemove() + suggest_news = messages.get_message(get_first_name(message), 'SUGGEST_NEWS') + await message.answer(suggest_news) + sleep(0.3) + suggest_news_2 = messages.get_message(get_first_name(message), 'SUGGEST_NEWS_2') + await message.answer(suggest_news_2, reply_markup=markup) + except Exception as e: + await message.bot.send_message(IMPORTANT_LOGS, + f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + + +@private_router.message( + ChatTypeFilter(chat_type=["private"]), + F.text == '👋🏼Сказать пока!' +) +@private_router.message( + ChatTypeFilter(chat_type=["private"]), + F.text == 'Выйти из чата' +) +async def end_message(message: types.Message, state: FSMContext): + try: + logger.info( + f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + name_stick_bye = list(Path('Stick').rglob('Universal_*')) + random_stick_bye = random.choice(name_stick_bye) + random_stick_bye = FSInputFile(path=random_stick_bye) + await message.answer_sticker(random_stick_bye) + except Exception as e: + logger.error( + f"Ошибка в функции end_message при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + await message.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + try: + markup = types.ReplyKeyboardRemove() + bye_message = messages.get_message(get_first_name(message), 'BYE_MESSAGE') + await message.answer(bye_message, reply_markup=markup) + await state.set_state("START") + except Exception as e: + logger.error( + f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + await message.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + + +@private_router.message( + StateFilter("SUGGEST"), + ChatTypeFilter(chat_type=["private"]), +) +async def suggest_router(message: types.Message, state: FSMContext, album: list = None): + logger.info( + f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + try: + if message.content_type == 'text': + lower_text = message.text.lower() + post_text, is_anonymous = get_text_message(lower_text, message.from_user.full_name, + message.from_user.username) + markup = get_reply_keyboard_for_post() + if is_anonymous: + await send_text_message(GROUP_FOR_POST, message, post_text, markup) + else: + await send_text_message(GROUP_FOR_POST, message, post_text, markup) + markup_for_user = get_reply_keyboard(BotDB, message.from_user.id) + success_send_message = messages.get_message(get_first_name(message), 'SUCCESS_SEND_MESSAGE') + await message.answer(success_send_message, reply_markup=markup_for_user) + await state.set_state("START") + elif message.content_type == 'photo' and message.media_group_id is None: + lower_caption = message.caption.lower() + markup = get_reply_keyboard_for_post() + post_caption, is_anonymous = get_text_message(lower_caption, message.from_user.full_name, + message.from_user.username) + #TODO: тут какая-то шляпа + if is_anonymous: + await send_photo_message(GROUP_FOR_POST, message, + message.photo[-1].file_id, post_caption, markup) + else: + await send_photo_message(GROUP_FOR_POST, message, + message.photo[-1].file_id, post_caption, markup) + markup_for_user = get_reply_keyboard(BotDB, message.from_user.id) + success_send_message = messages.get_message(get_first_name(message), 'SUCCESS_SEND_MESSAGE') + await message.answer(success_send_message, reply_markup=markup_for_user) + await state.set_state("START") + elif message.media_group_id is not None: + post_caption = " " + if album[0].caption: + lower_caption = album[0].caption.lower() + post_caption, is_anonymous = get_text_message(lower_caption, message.from_user.full_name, + message.from_user.username) + media_group = process_photo_album(album, post_caption) + media_group_message_id = await send_media_group_message(GROUP_FOR_POST, message, + media_group) + sleep(0.2) + markup = get_reply_keyboard_for_post() + help_message_id = await send_text_message(GROUP_FOR_POST, message, "^", markup) + await state.update_data(media_group_message_id=media_group_message_id, help_message_id=help_message_id) + + markup_for_user = get_reply_keyboard(BotDB, message.from_user.id) + success_send_message = messages.get_message(get_first_name(message), 'SUCCESS_SEND_MESSAGE') + await message.answer(success_send_message, reply_markup=markup_for_user) + await state.set_state("START") + else: + await message.bot.send_message(message.chat.id, + 'Я пока не умею работать с таким сообщением. Пришли текст и фото/фоты(ы)') + except Exception as e: + await message.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + + +@private_router.message( + ChatTypeFilter(chat_type=["private"]), + F.text == '🤪Хочу стикеры' +) +async def stickers(message: types.Message, state: FSMContext): + logger.info( + f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + markup = get_reply_keyboard(BotDB, message.from_user.id) + try: + BotDB.update_info_about_stickers(user_id=message.from_user.id) + await message.forward(chat_id=GROUP_FOR_LOGS) + await message.answer(text='Хорошо, лови, добавить можно отсюда: https://t.me/addstickers/love_biysk', + reply_markup=markup) + await state.set_state("START") + except Exception as e: + await message.bot.send_message(chat_id=IMPORTANT_LOGS, + text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") + logger.error( + f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + + +@private_router.message( + StateFilter("START"), + ChatTypeFilter(chat_type=["private"]), + F.text == '📩Связаться с админами' +) +async def connect_with_admin(message: types.Message, state: FSMContext): + logger.info( + f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") + admin_message = messages.get_message(get_first_name(message), 'CONNECT_WITH_ADMIN') + await message.answer(admin_message, parse_mode="html") + await message.forward(chat_id=GROUP_FOR_LOGS) + await state.set_state("PRE_CHAT") + + +@private_router.message( + StateFilter("PRE_CHAT"), + ChatTypeFilter(chat_type=["private"]), +) +@private_router.message( + StateFilter("CHAT"), + ChatTypeFilter(chat_type=["private"]), +) +async def resend_message_in_group_for_message(message: types.Message, state: FSMContext): + logger.info( + f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.message_id})") + await message.forward(chat_id=GROUP_FOR_MESSAGE) + current_date = datetime.now() + date = current_date.strftime("%Y-%m-%d %H:%M:%S") + BotDB.add_new_message_in_db(message.text, message.from_user.id, message.message_id + 1, date) + question = messages.get_message(get_first_name(message), 'QUESTION') + user_state = await state.get_state() + if user_state == "PRE_CHAT": + markup = get_reply_keyboard(BotDB, message.from_user.id) + await message.answer(question, reply_markup=markup) + await state.set_state("START") + elif user_state == "CHAT": + markup = get_reply_keyboard_leave_chat() + await message.answer(question, reply_markup=markup) + +# @private_router.message( +# ChatTypeFilter(chat_type=["private"]) +# ) +# async def default(message: types.Message, state: FSMContext): +# markup = get_reply_keyboard(BotDB, message.from_user.id) +# await message.answer('Кажется ты заблудился. Держи клавиатуру, твое состояние сброшено на начало', reply_markup=markup) +# await state.set_state("START") diff --git a/helper_bot/helper_bot.py b/helper_bot/helper_bot.py deleted file mode 100644 index 8010dfc..0000000 --- a/helper_bot/helper_bot.py +++ /dev/null @@ -1,793 +0,0 @@ -from datetime import datetime, timedelta -import random -import traceback -from enum import Enum -from pathlib import Path -from time import sleep -import telebot -from telebot import types -from telebot.apihelper import ApiTelegramException -from telebot.types import InputMediaPhoto - -import messages -from logs.custom_logger import Logger - -#Инициализируем логгер -bot_logger = Logger(name='bot') - - -class State(Enum): - START = "START" - SUGGEST = "SUGGEST" - ADMIN = "ADMIN" - CHAT = "CHAT" - PRE_CHAT = "PRE_CHAT" - - -class TelegramHelperBot: - def __init__(self, dependency_factory): - self.state = State.START - self.BotDB = dependency_factory.get_database() - self.settings = dependency_factory.get_settings() - token = self.settings['Telegram']['bot_token'] - self.GROUP_FOR_POST = self.settings['Telegram']['group_for_posts'] - self.GROUP_FOR_MESSAGE = self.settings['Telegram']['group_for_message'] - self.MAIN_PUBLIC = self.settings['Telegram']['main_public'] - self.GROUP_FOR_LOGS = self.settings['Telegram']['group_for_logs'] - self.IMPORTANT_LOGS = self.settings['Telegram']['important_logs'] - self.PREVIEW_LINK = self.settings['Telegram']['preview_link'] - self.LOGS = self.settings['Settings']['logs'] - self.TEST = self.settings['Settings']['test'] - self.bot = telebot.TeleBot(token) - self.logger = bot_logger.get_logger() - - # Router for user - @self.bot.message_handler(func=lambda message: True, chat_types=['private']) - def handle_message(message): - self.logger.info( - f'Получено сообщение: {message.text} от пользователя: {message.from_user.full_name} id юзера: {message.chat.id}') - if self.BotDB.check_user_in_blacklist(message.from_user.id): - attribute = self.BotDB.get_blacklist_users_by_id(message.from_user.id) - self.bot.send_message(message.chat.id, - f'Ты заблокирован\nПричина блокировки: {attribute[2]}\nДата разблокировки: {attribute[3]}', - parse_mode='HTML') - self.logger.info(f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) заблокирован') - return - if self.state == State.START: - if message.text == '/start': - self.start_message(message) - elif message.text == '📢Предложить свой пост': - self.suggest_post(message) - self.state = State.SUGGEST - elif message.text == '🤪Хочу стикеры': - self.stickers(message) - self.state = State.START - elif message.text == '📩Связаться с админами': - self.connect_with_admin(message) - self.state = State.PRE_CHAT - elif message.text == '👋🏼Сказать пока!': - self.end_message(message) - self.state = State.START - elif message.text == 'Выйти из чата': - self.end_message(message) - elif message.text == '/admin': - access = self.check_access(message.from_user.id) - if access: - self.admin_panel(message) - self.state = State.ADMIN - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вошел в админ-панель') - else: - self.bot.send_message(message.chat.id, 'Доступ запрещен, досвидания!') - self.logger.info( - f'Пользователю {message.from_user.full_name} (ID: {message.chat.id}) отказано в доступе к админ-панели') - elif message.text == '/state': - self.bot.send_message(message.chat.id, - f'Твой state == {self.state.value}') - else: - self.bot.send_message(message.chat.id, - #TODO: Здесь раньше был /state - "Не понимаю где ты находишься. Нажми /start, и я перезапущусь") - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) отправил непонятное сообщение: {message.text}') - - if self.state == State.SUGGEST: - self.bot.register_next_step_handler(message, self.suggest_router) - self.state = State.START - if message.text == '/start': - self.state = State.START - self.start_message(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вернулся в главное меню') - if self.state == State.PRE_CHAT: - self.bot.register_next_step_handler(message, self.resend_message_in_group_for_message) - self.state = State.START - if message.text == '/start': - self.state = State.START - self.start_message(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вернулся в главное меню') - - if self.state == State.CHAT: - if message.text == 'Выйти из чата': - self.state = State.START - self.end_message(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вышел из чата') - elif message.text == '/start': - self.state = State.START - self.start_message(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вернулся в главное меню') - else: - self.resend_message_in_group_for_message(message) - - if self.state == State.ADMIN: - if message == '/admin' or message == '/restart' or message == 'Вернуться в админку': - access = self.check_access(message.from_user.id) - if access: - self.admin_panel(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вошел в админ-панель') - else: - self.bot.send_message(message.chat.id, 'Доступ запрещен, досвидания!') - self.logger.info( - f'Пользователю {message.from_user.full_name} (ID: {message.chat.id}) отказано в доступе к админ-панели') - if message.text == '/start': - self.state = State.START - self.start_message(message) - self.logger.info( - f'Пользователь {message.from_user.full_name} (ID: {message.chat.id}) вернулся в главное меню') - - @self.bot.message_handler(func=lambda message: True, chat_types=['group']) - def handle_message(message): - """Функция ответа админа пользователю через закрытый чат""" - self.logger.info( - f'Получено сообщение в группе {message.chat.title} (ID: {message.chat.id}) от пользователя {message.from_user.full_name} (ID: {message.from_user.id}): "{message.text}"') - self.state = State.CHAT - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("Выйти из чата") - markup.add(item1) - message_id = 0 - try: - message_id = message.reply_to_message.id - except AttributeError: - self.bot.send_message(message.chat.id, f'Блять, выдели сообщение!') - self.logger.warning( - f'В группе {message.chat.title} (ID: {message.chat.id}) админ не выделил сообщение для ответа.') - message_from_admin = message.text - try: - chat_id = self.BotDB.get_user_by_message_id(message_id) - self.bot.send_message(chat_id, message_from_admin, reply_markup=markup) - self.logger.info(f'Ответ админа "{message.text}" отправлен пользователю с ID: {chat_id}.') - except TypeError: - self.bot.send_message(message.chat.id, f'Не могу найти кому ответить в базе, проебали сообщение.') - self.logger.error( - f'Ошибка при поиске пользователя в базе для ответа: {message.text} в группе {message.chat.title} (ID: {message.chat.id})') - - # Админка - @self.bot.callback_query_handler(func=lambda call: call.data in ['publish', 'decline']) - def post_for_group(call): - self.logger.info( - f'Получен callback-запрос с данными: {call.data} от пользователя {call.from_user.full_name} (ID: {call.from_user.id})') - if call.data == 'publish' and call.message.content_type == 'text': - try: - self.bot.send_message(chat_id=self.MAIN_PUBLIC, text=call.message.text) - self.bot.delete_message(chat_id=self.GROUP_FOR_POST, message_id=call.message.message_id) - self.logger.info(f'Текст сообщения опубликован в канале {self.MAIN_PUBLIC}.') - except Exception as e: - if self.LOGS: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - self.logger.error(f'Ошибка при публикации текста в канал {self.MAIN_PUBLIC}: {str(e)}') - elif call.data == 'publish' and call.message.content_type == 'photo': - try: - self.bot.send_photo( - chat_id=self.MAIN_PUBLIC, - caption=call.message.caption, - photo=call.message.photo[-1].file_id, - ) - self.bot.delete_message(chat_id=self.GROUP_FOR_POST, message_id=call.message.message_id) - self.logger.info(f'Пост с фото опубликован в канале {self.MAIN_PUBLIC}.') - except Exception as e: - if self.LOGS: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - self.logger.error(f'Ошибка при публикации фотографии в канал {self.MAIN_PUBLIC}: {str(e)}') - elif call.data == 'decline': - try: - self.bot.delete_message(chat_id=self.GROUP_FOR_POST, message_id=call.message.message_id) - self.logger.info( - f'Сообщение отклонено админом {call.from_user.full_name} (ID: {call.from_user.id}).') - except Exception as e: - if self.LOGS: - self.bot.send_message(self.IMPORTANT_LOGS, - f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - self.logger.error(f'Ошибка при удалении сообщения в группе {self.GROUP_FOR_POST}: {str(e)}') - - @self.bot.callback_query_handler(func=lambda call: True) - def pagination(call): - if call.data[:3] == 'ban': - user_id = call.data[4:] - self.logger.info(f"Бан пользователя с ID: {user_id}") - self.ban_user(call.message, user_id) - if call.data == 'return': - self.logger.info(f"Возврат в админ панель") - self.bot.delete_message(call.message.chat.id, call.message.message_id) - self.admin_panel(call.message) - if call.data[:5] == 'unban': - user_id = call.data[6:] - self.delete_user_blacklist(user_id) - msg = f'Успешно удалено.' - self.bot.send_message(chat_id=call.message.chat.id, text=msg) - self.logger.info(f"Разблокирован пользователь с ID: {user_id}") - elif call.data[:4] == 'page': - page_number = int(call.data[5:]) - self.logger.info(f"Переход на страницу {page_number}") - if call.message.text == 'Список пользователей которые последними обращались к боту': - list_users = self.BotDB.get_last_users_from_db() - keyboard = self.create_keyboard_with_pagination(int(page_number), len(list_users), list_users, - 'ban') - self.bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, - reply_markup=keyboard) - elif "Список заблокированных пользователей".lower() in call.message.text.lower(): - #Готовим сообщения - message_user = self.get_banned_users_list(int(page_number) * 7 - 7) - self.bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id, - text=message_user) - - #Готовим клавиатуру - buttons = self.get_banned_users_buttons() - keyboard = self.create_keyboard_with_pagination(int(call.data[5:]), len(buttons), buttons, 'unban') - self.bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, - reply_markup=keyboard) - else: - self.logger.warning(f"Неизвестный callback data: {call.data}") - - def start(self): - while True: - try: - print(self.bot.last_update_id) - self.bot.polling(none_stop=True) - except ConnectionError as e: - self.logger.error( - f"Произошла ошибка (потеря коннекта): {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - except Exception as e: - self.logger.error(f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - def unban_notifier(self): - # Получение сегодняшней даты в формате DD-MM-YYYY - current_date = datetime.now() - print('Мы в функции unban_notifier') - today = current_date.strftime("%d-%m-%Y") - # Получение списка разблокированных пользователей - unblocked_users = self.BotDB.get_users_for_unblock_today(today) - message = "Разблокированные пользователи:\n" - for user_id, user_name in unblocked_users.items(): - message += f"ID: {user_id}, Имя: {user_name}\n" - - # Отправка сообщения в канал - self.bot.send_message(self.GROUP_FOR_MESSAGE, message) - - # Черный список - def admin_panel(self, message): - try: - self.logger.info(f"Запуск админ панели для пользователя: {message.from_user.id}") - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("Бан (Список)") - #item2 = types.KeyboardButton("Добавить админа") #TODO: Когда-нибудь потом доделаю - #item3 = types.KeyboardButton("Удалить админа") - item4 = types.KeyboardButton("Разбан (список)") - item5 = types.KeyboardButton("Вернуться в бота") - markup.add(item1, item4, item5) - self.bot.send_message(message.chat.id, "Добро пожаловать в админку. Выбери что хочешь:", - reply_markup=markup) - self.bot.register_next_step_handler(message, self.handle_admin_message) - except Exception as e: - self.logger.error(f"Ошибка при запуске админ панели: {e}") - self.bot.register_next_step_handler(message, self.admin_panel) - - def handle_admin_message(self, message): - self.logger.info(f"Получено сообщение от админа: {message.text} (пользователь: {message.from_user.id})") - try: - if message.text == "Бан (Список)": - self.get_last_users(message) - elif message.text == "Разбан (список)": - self.get_banned_users(message) - elif message.text == "Вернуться в бота": - self.start_message(message) - else: - self.logger.warning(f"Неизвестное сообщение от админа: {message.text}") - self.bot.reply_to(message, "Неизвестная команда.") - except Exception as e: - self.logger.error(f"Ошибка при обработке сообщения админа: {e}") - self.bot.reply_to(message, f"Ошибка\n\n {e}") - self.admin_panel(message) - - def ban_user(self, message, user_id: int): - self.logger.info( - f"Получена команда от админа на бан пользователя: {message.text} (пользователь: {message.from_user.id})") - user_name = self.BotDB.get_username(user_id=user_id) - ban_object = {'user_id': user_id, 'user_name': user_name, 'message_for_user': None, 'date_to_unban': None} - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("Спам") - item2 = types.KeyboardButton("Заебал стикерами") - markup.add(item1, item2) - self.bot.send_message(message.chat.id, - f"Выбран пользователь: {user_id}. Выбери причину бана из списка или напиши ее в чат", - reply_markup=markup) - self.bot.register_next_step_handler(message, self.ban_user_step_2, ban_object) - - def ban_user_step_2(self, message, ban_object: dict): - self.logger.info(f"Переход на шаг 2 бана пользователя. Словарь с данными для бана: {ban_object})") - ban_object['message_for_user'] = message.text - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("1") - item2 = types.KeyboardButton("7") - item3 = types.KeyboardButton("30") - item4 = types.KeyboardButton("Навсегда") - markup.add(item1, item2, item3, item4) - self.bot.send_message(message.chat.id, f"Выбрана причина: {message.text}. Выбери срок бана в днях или напиши " - f"его в чат", - reply_markup=markup) - self.bot.register_next_step_handler(message, self.ban_user_step_3, ban_object) - - def ban_user_step_3(self, message, ban_object: dict): - self.logger.info(f"Переход на шаг 3 бана пользователя. Словарь с данными для бана: {ban_object})") - date_to_unban = None - if message.text != 'Навсегда': - date_to_unban = self.add_days_to_date(message.text) - else: - pass - ban_object['date_to_unban'] = date_to_unban - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("Подтвердить") - item2 = types.KeyboardButton("Отменить") - markup.add(item1, item2) - self.bot.send_message(message.chat.id, - f"Необходимо подтверждение:\nПользователь:{ban_object['user_id']}\nПричина бана:{ban_object['message_for_user']}.\nСрок бана:{ban_object['date_to_unban']}", - parse_mode='html', - reply_markup=markup) - self.bot.register_next_step_handler(message, self.ban_user_final_step, ban_object) - - def ban_user_final_step(self, message, ban_object: dict): - self.logger.info(f"Переход на финальный шаг бана пользователя. Словарь с данными для бана: {ban_object})") - if message.text == 'Подтвердить': - exists = self.BotDB.check_user_in_blacklist(ban_object['user_id']) - if exists: - self.bot.reply_to(message, f"Пользователь уже был заблокирован ранее.") - self.logger.info(f"Пользователь: {ban_object['user_id']} был заблокирован ранее)") - self.admin_panel(message) - else: - self.BotDB.set_user_blacklist(ban_object['user_id'], - ban_object['user_name'], - ban_object['message_for_user'], - ban_object['date_to_unban']) - self.bot.reply_to(message, f"Пользователь {ban_object['user_name']} успешно заблокирован.") - self.logger.info(f"Пользователь: {ban_object['user_id']} успешно заблокирован)") - self.admin_panel(message) - - def get_last_users(self, message): - self.logger.info( - f"Попытка получения списка последних пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})") - list_users = self.BotDB.get_last_users_from_db() - keyboard = self.create_keyboard_with_pagination(1, len(list_users), list_users, 'ban') - self.bot.send_message(chat_id=message.chat.id, text="Список пользователей которые последними обращались к боту", - reply_markup=keyboard) - - def get_banned_users(self, message): - self.logger.info( - f"Попытка получения списка заблокированных пользователей. Текст сообщения: {message.text} Имя автора сообщения: {message.from_user.full_name})") - message_text = self.get_banned_users_list(0) - buttons_list = self.get_banned_users_buttons() - if buttons_list: - k = self.create_keyboard_with_pagination(1, len(buttons_list), buttons_list, 'unban') - self.bot.send_message(message.chat.id, message_text, reply_markup=k) - else: - self.bot.send_message(message.chat.id, "В списке забанненых пользователей никого нет") - self.admin_panel(message) - - def start_message(self, message): - try: - self.logger.info( - f"Формирование приветственного сообщения для пользователя. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name})") - name_stick_hello = list(Path('Stick').rglob('Hello_*')) - random_stick_hello = open(random.choice(name_stick_hello), 'rb') - self.logger.info(f"Стикер успешно получен из БД") - # logging - if self.LOGS: - self.bot.forward_message(chat_id=self.GROUP_FOR_LOGS, - from_chat_id=message.chat.id, - message_id=message.message_id) - self.bot.send_sticker(message.chat.id, random_stick_hello) - sleep(0.3) - except Exception as e: - self.logger.error(f"Произошла ошибка при получении стикера. Ошибка: {str(e)}") - if self.LOGS: - self.bot.send_message(self.IMPORTANT_LOGS, - f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - try: - self.logger.info( - f"Получение данных для приветственного сообщения пользователю. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name})") - user_id = message.from_user.id - first_name = message.from_user.first_name - full_name = message.from_user.full_name - is_bot = message.from_user.is_bot - username = message.from_user.username - language_code = message.from_user.language_code - current_date = datetime.now() - date = current_date.strftime("%Y-%m-%d %H:%M:%S") - if not self.BotDB.user_exists(user_id): - self.BotDB.add_new_user_in_db(user_id, first_name, full_name, username, is_bot, language_code, date, - date) - self.BotDB.update_date_for_user(date, user_id) - markup = self.get_reply_keyboard(message) - hello_message = messages.get_message(self.__get_first_name(message), 'HELLO_MESSAGE') - self.bot.send_message(message.chat.id, hello_message, parse_mode='html', reply_markup=markup, - disable_web_page_preview=not self.PREVIEW_LINK) - except Exception as e: - self.logger.error( - f"Произошла ошибка при отправке приветственного сообщения для пользователя {message.from_user.id} Имя: {message.from_user.full_name}. Ошибка: {str(e)}") - if self.LOGS: - self.bot.send_message(self.IMPORTANT_LOGS, - f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - def resend_message_in_group_for_message(self, message): - self.logger.info( - f"Попытка пересылки сообщения в связь с админами. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.id})") - self.bot.forward_message(chat_id=self.GROUP_FOR_MESSAGE, - from_chat_id=message.chat.id, - message_id=message.message_id - ) - current_date = datetime.now() - date = current_date.strftime("%Y-%m-%d %H:%M:%S") - self.BotDB.add_new_message_in_db(message.text, message.from_user.id, message.message_id + 1, date) - question = messages.get_message(self.__get_first_name(message), 'QUESTION') - markup = self.get_reply_keyboard(message) - self.bot.send_message(message.chat.id, question, parse_mode='html', - disable_web_page_preview=not self.PREVIEW_LINK, - reply_markup=markup) - - def suggest_post(self, message): - try: - self.logger.info( - f"Вызов функции suggest_post. Сообщение: {message.text} Имя автора сообщения: {message.from_user.full_name} Идентификатор сообщения: {message.id})") - markup = types.ReplyKeyboardRemove() - suggest_news = messages.get_message(self.__get_first_name(message), 'SUGGEST_NEWS') - self.bot.send_message(message.chat.id, suggest_news, parse_mode='html') - sleep(0.3) - suggest_news_2 = messages.get_message(self.__get_first_name(message), 'SUGGEST_NEWS_2') - self.bot.send_message(message.chat.id, suggest_news_2, parse_mode='html', reply_markup=markup) - except Exception as e: - self.bot.send_message(self.IMPORTANT_LOGS, - f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - def stickers(self, message): - self.logger.info( - f"Вызов функции stickers. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - self.BotDB.update_info_about_stickers(user_id=message.from_user.id) - markup = self.get_reply_keyboard(message) - try: - self.bot.forward_message(chat_id=self.GROUP_FOR_LOGS, - from_chat_id=message.chat.id, - message_id=message.message_id) - self.bot.send_message(message.chat.id, - text='Хорошо, лови, добавить можно отсюда: https://t.me/addstickers/love_biysk', - reply_markup=markup) - except ApiTelegramException as e: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - self.logger.error( - f"Ошибка функции stickers. Ошибка: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - - def connect_with_admin(self, message): - self.logger.info( - f"Вызов функции connect_with_admin. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - connect_with_admin = messages.get_message(self.__get_first_name(message), 'CONNECT_WITH_ADMIN') - self.bot.send_message(message.chat.id, connect_with_admin, parse_mode="html") - # logging - if self.LOGS: - self.bot.forward_message(chat_id=self.GROUP_FOR_LOGS, - from_chat_id=message.chat.id, - message_id=message.message_id) - - def end_message(self, message): - try: - self.logger.info( - f"Вызов функции end_message. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - name_stick_bye = list(Path('Stick').rglob('Universal_*')) - random_stick_bye = open(random.choice(name_stick_bye), 'rb') - self.bot.send_sticker(message.chat.id, random_stick_bye) - except ApiTelegramException as e: - self.logger.error( - f"Ошибка в функции stickers при получении стикера: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - if self.LOGS: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - markup = types.ReplyKeyboardRemove() - try: - bye_message = messages.get_message(self.__get_first_name(message), 'BYE_MESSAGE') - self.bot.send_message(message.chat.id, bye_message, - parse_mode='html', reply_markup=markup, - disable_web_page_preview=not self.PREVIEW_LINK) - except Exception as e: - if self.LOGS: - self.logger.error( - f"Ошибка в функции stickers при получении сообщения: {str(e)} Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - #TODO: deprecated - def send_to_suggest_2(self, message): - markup = self._get_reply_keyboard_for_post() - try: - if message.content_type == 'text': - post_text = message.text.lower() - if post_text.find('неанон') != -1 or post_text.find('не анон') != -1: - self.bot.send_message( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - text=f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {message.from_user.first_name} @{message.from_user.username}', - reply_markup=markup - ) - elif post_text.find('анон') != -1: - self.bot.send_message( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - text=f'Пост из ТГ:\n{message.text}\n\nПост опубликован анонимно', - reply_markup=markup - ) - else: - self.bot.send_message( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - text=f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {message.from_user.first_name} @{message.from_user.username}', - reply_markup=markup - ) - elif message.content_type == 'photo' and message.media_group_id is None: - post_text_for_photo = message.caption.lower() - if post_text_for_photo.find('неанон') != -1 or post_text_for_photo.find('не анон') != -1: - self.bot.send_photo( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - caption=f'Пост из ТГ:\n{post_text_for_photo}\n\nАвтор поста: {message.from_user.first_name} @{message.from_user.username}', - photo=message.photo[-1].file_id, - reply_markup=markup - ) - elif post_text_for_photo.find('анон') != -1 or post_text_for_photo.find('анон') != -1: - self.bot.send_photo( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - caption=f'Пост из ТГ:\n{post_text_for_photo}\n\nПост опубликован анонимно', - photo=message.photo[-1].file_id, - reply_markup=markup - ) - else: - self.bot.send_photo( - # TODO: GROUP_FOR_POST - chat_id=self.GROUP_FOR_POST, - caption=f'Пост из ТГ:\n{post_text_for_photo}\n\nАвтор поста: {message.from_user.first_name} @{message.from_user.username}', - photo=message.photo[-1].file_id, - reply_markup=markup - ) - # TODO: Не понятна реализация с альбомами от слова совсем. 11.07 Реализация понятна. Нужно отправлять альбомы + сообщение в одном сообщении. Следующее сообщение с пробелом и кнопками - # elif message.content_type == 'photo' and message.media_group_id != None: - # bot.forward_message(chat_id=IMPORTANT_LOGS, from_chat_id=message.chat.id, message_id=message.message_id ) - else: - pass - except Exception as e: - if self.LOGS: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - markup_for_user = self.get_reply_keyboard(message) - success_send_message = messages.get_message(self.__get_first_name(message), 'SUCCESS_SEND_MESSAGE') - self.bot.send_message(message.chat.id, success_send_message, parse_mode='html', - disable_web_page_preview=not self.PREVIEW_LINK, reply_markup=markup_for_user) - - def suggest_router(self, message): - self.logger.info( - f"Вызов функции suggest_router. Пользователь: {message.from_user.id} Имя автора сообщения: {message.from_user.full_name}") - try: - if message.content_type == 'text': - lower_text = message.text.lower() - post_text, is_anonymous = self._get_text_message(lower_text, message.from_user.full_name, - message.from_user.id) - if is_anonymous: - self._send_text_message(post_text) - else: - self._send_text_message(post_text) - markup_for_user = self.get_reply_keyboard(message) - success_send_message = messages.get_message(self.__get_first_name(message), 'SUCCESS_SEND_MESSAGE') - self.bot.send_message(message.chat.id, success_send_message, parse_mode='html', - disable_web_page_preview=not self.PREVIEW_LINK, reply_markup=markup_for_user) - elif message.content_type == 'photo' and message.media_group_id is None: - lower_caption = message.caption.lower() - post_caption, is_anonymous = self._get_text_message(lower_caption, message.from_user.full_name, - message.from_user.id) - if is_anonymous: - self._send_photo_message(message.photo[-1].file_id, post_caption) - else: - self._send_photo_message(message.photo[-1].file_id, post_caption) - markup_for_user = self.get_reply_keyboard(message) - success_send_message = messages.get_message(self.__get_first_name(message), 'SUCCESS_SEND_MESSAGE') - self.bot.send_message(message.chat.id, success_send_message, parse_mode='html', - disable_web_page_preview=not self.PREVIEW_LINK, reply_markup=markup_for_user) - elif message.media_group_id is not None: - self.bot.send_message(message.chat.id, - 'Я пока не умею работать с несколькими файлами. Пришли текст и не более одного фото') - self.bot.register_next_step_handler(message, self.suggest_router) - else: - self.bot.send_message(message.chat.id, - 'Я пока не умею работать с таким сообщением. Пришли текст и не более одного фото') - self.bot.register_next_step_handler(message, self.suggest_router) - except Exception as e: - if self.LOGS: - self.bot.send_message(chat_id=self.IMPORTANT_LOGS, - text=f"Произошла ошибка: {str(e)}\n\nTraceback:\n{traceback.format_exc()}") - - @staticmethod - def _get_reply_keyboard_for_post(): - markup = types.InlineKeyboardMarkup(row_width=1) - item1 = types.InlineKeyboardButton("Опубликовать", callback_data='publish') - item2 = types.InlineKeyboardButton("Отклонить", callback_data='decline') - markup.add(item1, item2) - return markup - - @staticmethod - def _get_text_message(post_text: str, first_name: str, username: str): - """ - Форматирует текст сообщения для публикации в зависимости от наличия ключевых слов "анон" и "неанон". - - Args: - post_text: Текст сообщения - first_name: Имя автора поста - username: Юзернейм автора поста - - Returns: - Кортеж из двух элементов: - - Сформированный текст сообщения. - - Флаг, указывающий, является ли пост анонимным (True - анонимный, False - не анонимный). - """ - if "неанон" in post_text or "не анон" in post_text: - is_anonymous = False - return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}', is_anonymous - elif "анон" in post_text: - is_anonymous = True - return f'Пост из ТГ:\n{post_text}\n\nПост опубликован анонимно', is_anonymous - else: - is_anonymous = False - return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}', is_anonymous - - def _send_text_message(self, post_text: str): - markup = self._get_reply_keyboard_for_post() - self.bot.send_message( - chat_id=self.GROUP_FOR_POST, - text=post_text, - parse_mode='html', - disable_web_page_preview=not self.PREVIEW_LINK, - reply_markup=markup - ) - - def _send_photo_message(self, photo: str, post_text: str): - markup = self._get_reply_keyboard_for_post() - self.bot.send_photo( - chat_id=self.GROUP_FOR_POST, - caption=post_text, - photo=photo, - reply_markup=markup - ) - - def get_reply_keyboard(self, message): - markup = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True) - item1 = types.KeyboardButton("📢Предложить свой пост") - item2 = types.KeyboardButton("📩Связаться с админами") - item3 = types.KeyboardButton("👋🏼Сказать пока!") - item4 = types.KeyboardButton("🤪Хочу стикеры") if not self.BotDB.get_info_about_stickers( - user_id=message.from_user.id) else None - - if item4: - markup.add(item1, item2, item3, item4) - else: - markup.add(item1, item2, item3) - - return markup - - @staticmethod - def __get_first_name(message): - return message.from_user.first_name - - def check_access(self, user_id: int): - """Проверка прав на совершение действий""" - return self.BotDB.is_admin(user_id) - - @staticmethod - def add_days_to_date(days): - """Прибавляет указанное количество дней к текущей дате и возвращает дату в формате DD-MM-YYYY.""" - current_date = datetime.now() - future_date = current_date + timedelta(days=int(days)) - formatted_date = future_date.strftime("%d-%m-%Y") - return formatted_date - - @staticmethod - def create_keyboard_with_pagination(page: int, total_items: int, array_items: list[tuple[any, any]], callback: str): - """ - Создает клавиатуру с пагинацией для заданного набора элементов и устанавливает необходимый callback - - Args: - page: Номер текущей страницы. - total_items: Общее количество элементов. - array_items: Лист кортежей. Содержит в себе user_name: user_id - callback: Действие в коллбеке. Вернет callback вида ({callback}_{user_id}) - - Returns: - InlineKeyboardMarkup: Клавиатура с кнопками пагинации. - """ - - # Определяем общее количество страниц - total_pages = (total_items + 7 - 1) // 7 - - page = page - # Создаем список кнопок - buttons = [] - # Вычисляем стартовый номер для текущей страницы - start_index = (page - 1) * 7 #тут было +1, убрал, потому что на текстовом массиве выходит за пределы - # Кнопки с номерами страниц - for i in range(start_index, min(start_index + 7, - len(array_items))): #тут было len(array_items) +1, убрал, потому что на текстовом массиве выходит за пределы - buttons.append( - types.InlineKeyboardButton(f"{array_items[i][0]}", callback_data=f"{callback}_{array_items[i][1]}")) - - # Добавляем кнопки "Предыдущая" и "Следующая" - if int(page) > 1: - buttons.insert(6, types.InlineKeyboardButton("⬅️ Предыдущая", callback_data=f"page_{page - 1}")) - if page < total_pages: - buttons.append(types.InlineKeyboardButton("➡️ Следующая", callback_data=f"page_{page + 1}")) - #Добавляем кнопку назад - buttons.append(types.InlineKeyboardButton("🏠 Назад", callback_data="return")) - # Формируем клавиатуру с 3 кнопками в ряд - keyboard = [] - for i in range(0, len(buttons), 3): - keyboard.append(buttons[i:i + 3]) - return types.InlineKeyboardMarkup(keyboard) - - def get_banned_users_list(self, offset: int): - """ - Возвращает сообщение со списком пользователей и словарь с ником + идентификатором - - Args: - offset: отступ для запроса в базу данных - - Returns: - message - текст сообщения - user_ids - лист кортежей [(user_name: user_id)] - """ - users = self.BotDB.get_banned_users_from_db_with_limits(limit=7, offset=offset) - message = "Список заблокированных пользователей:\n" - - for user in users: - message += f"Пользователь: {user[0]}\n" - message += f"Причина бана: {user[2]}\n" - message += f"Дата разбана: {user[3]}\n\n" - return message - - def get_banned_users_buttons(self): - """ - Возвращает сообщение со списком пользователей и словарь с ником + идентификатором - - Args: - offset: отступ для запроса в базу данных - - Returns: - message - текст сообщения - user_ids - лист кортежей [(user_name: user_id)] - """ - users = self.BotDB.get_banned_users_from_db() - user_ids = [] - - for user in users: - user_ids.append((user[0], user[1])) - return user_ids - - def delete_user_blacklist(self, user_id): - return self.BotDB.delete_user_blacklist(user_id=user_id) diff --git a/helper_bot/keyboards/__init__.py b/helper_bot/keyboards/__init__.py new file mode 100644 index 0000000..734fb54 --- /dev/null +++ b/helper_bot/keyboards/__init__.py @@ -0,0 +1 @@ +from .main import get_reply_keyboard_for_post, get_reply_keyboard \ No newline at end of file diff --git a/helper_bot/keyboards/__pycache__/__init__.cpython-312.pyc b/helper_bot/keyboards/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..128ff0f8f6ee49df0f5d3d35b7f75308a8355369 GIT binary patch literal 259 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 literal 0 HcmV?d00001 diff --git a/helper_bot/middlewares/__pycache__/text_middleware.cpython-312.pyc b/helper_bot/middlewares/__pycache__/text_middleware.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9fecc1b01178e092c5a2fc2f32ab99889bb9742 GIT binary patch literal 2735 zcmbVOZ)g-p6rb6Dx4A#L#F)RvW;Ip1!Nfl#rdCbV#0IKHK}0x&Wpg{1T->`$XE*Al zCrS;HfTE>PV_Pu&)QBzZNBtJ@OQF!_jBx3eQi~MYZ&zYL{M0wQw@D0vwhpsz-kW*z z=FQCSz1i<0ksyMS{qf_()iQ*hF~KiliCOFi<`y!LflU-+Eob5s7voZVj86$M0kb~d z6jM@6a(TfFq~w^45r_JaA&w(MBGNtHvllDNVm0t56Z%!&;SZ*>j?kNo+m3iDoy??F z?35qUEpjYr7$!NdQvyAKW5lxbK@#_DcvLD0B?k$LVFSfDqs+jBCjt=@Ts^2HID;D( zW0Jv>0Pw=N5R(~ih@^}JNyyUz7efij2*7AKE2}{#de9sgO)Y!j1Wer~>3BBIt#E?z zY-5WGkXtB+b4UZuD@gtxC0h5s3=+^N7D8y+ZUkU-9B8lyZvFN zxvJ#Q@X8}Ajh@4FgMkN85{_)G@f=>u);^g1fAkpu49w`TkLS|*tjXbfrR%}S4)_DF zXWzGU5~e8OUtap+Qa-f%X-D_$y1Dk=iQXH>Cy!4ZE;R1QLk;gNN~kPyBRUzK z5({wcuh^ax5mf9DlC_!7BiV4*;^C zaJTrYqKHm1I57yXDml)-(2Ih-1f5|&=d93%`SJ>}#QL*WuLnR2`Pwiu?DNa*+BI4VKKX0@JXWQ#w}O7~9Mh9&WjT#0W@a!MUyhW1x*q(FFJ)K|7+(e7VgnFB z1zHOgClO_PfOjQW3JVA=knjbI%~#w2dMWQS_0)i&ca62I4jE00>!zt`ZTrj&a8?&E zp6+g#Zv}x|Ml+kf*>-o^W#obxtn+6{))C!kRApKXIs;T(fM+!$WT4sBfsuLGgsjXO=Zn`WvD>i#^``mXz9`TF-J$`|CCg1jLQ zRoOi!Z+IeiF4S$E5GGDfMqpN?a%E9z(URv$2&kyT+w&2fjPm}O@CvXp|aL*>;;rc@o2#*?YPqXl7gUI+zn9*iN`&B&n z>1|dXbgL2e)TSZ5Ynl_(v{c3zH5nh)wD(7K(;J~Ir8s;t4Zl4JGo#y-y}Oj%kc$e+ zQk=tEHl-{%Q5DEt4@&OUQTA55gykNq56C$Bi{r(O)8ce|rek_Iuk2hzAQ!_C@m)OA zw}@C Any: + """ + Main middleware logic. + """ + # # If the event has no media_group_id, pass it to the handler immediately + key = (event.chat.id, event.from_user.id) + if not event.text: + return await handler(event, data) + + self.texts[key].append(event) + total_before = len(self.texts[key]) + # # Wait for a specified latency period + await asyncio.sleep(self.latency) + # + # # Check the total number of messages after the latency + total_after = len(self.texts[key]) + # + # # If new messages were added during the latency, exit + if total_before != total_after: + return + # + # # Sort the album messages by message_id and add to data + msg_texts = self.texts[key] + msg_texts.sort(key=lambda x: x.message_id) + data["texts"] = ''.join([msg.text for msg in msg_texts]) + # + # Remove the media group from tracking to free up memory + del self.texts[key] + # # Call the original event handler + return await handler(event, data) +# diff --git a/helper_bot/middlewares/text_middleware.py b/helper_bot/middlewares/text_middleware.py new file mode 100644 index 0000000..627d2bd --- /dev/null +++ b/helper_bot/middlewares/text_middleware.py @@ -0,0 +1,61 @@ +import asyncio +from typing import Any, Dict, Union + +from aiogram import BaseMiddleware +from aiogram.types import Message + + +class AlbumMiddleware(BaseMiddleware): + def __init__(self, latency: Union[int, float] = 0.1): + # Initialize latency and album_data dictionary + self.latency = latency + self.album_data = {} + + # + def collect_album_messages(self, event: Message): + """ + Collect messages of the same media group. + """ + # # Check if media_group_id exists in album_data + if event.media_group_id not in self.album_data: + # # Create a new entry for the media group + self.album_data[event.media_group_id] = {"messages": []} + # + # # Append the new message to the media group + self.album_data[event.media_group_id]["messages"].append(event) + # + # # Return the total number of messages in the current media group + return len(self.album_data[event.media_group_id]["messages"]) + + # + async def __call__(self, handler, event: Message, data: Dict[str, Any]) -> Any: + """ + Main middleware logic. + """ + # # If the event has no media_group_id, pass it to the handler immediately + if not event.media_group_id: + return await handler(event, data) + # + # # Collect messages of the same media group + total_before = self.collect_album_messages(event) + # + # # Wait for a specified latency period + await asyncio.sleep(self.latency) + # + # # Check the total number of messages after the latency + total_after = len(self.album_data[event.media_group_id]["messages"]) + # + # # If new messages were added during the latency, exit + if total_before != total_after: + return + # + # # Sort the album messages by message_id and add to data + album_messages = self.album_data[event.media_group_id]["messages"] + album_messages.sort(key=lambda x: x.message_id) + data["album"] = album_messages + # + # # Remove the media group from tracking to free up memory + del self.album_data[event.media_group_id] + # # Call the original event handler + return await handler(event, data) +# diff --git a/helper_bot/utils/__init__.py b/helper_bot/utils/__init__.py new file mode 100644 index 0000000..b9f7655 --- /dev/null +++ b/helper_bot/utils/__init__.py @@ -0,0 +1 @@ +from .state import StateUser diff --git a/helper_bot/utils/__pycache__/__init__.cpython-312.pyc b/helper_bot/utils/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d401561b5fe0ad79549b6786018751dc00359b35 GIT binary patch literal 206 zcmX@j%ge<81SZ{+(&T{jV-N=hn4pZ$0zk%eh7^Vr#vF!R#wbQchDs()=9i2>VNJ$c zoWUiDC8?pssYQO8Ot)BzLA)YnpxjD^&ma?iIlEZJ1XLzxBo^fc6y;~7CYKb)l%(dQ zrWYmV>SmjECgqpJK$!6$W@$-gPH{|pd}dx|NqoFsLFF$Fo80`A(wtPgA`YOL VAghZ(j1SC=jEwgfWQy2;8~}+4HXHx| literal 0 HcmV?d00001 diff --git a/helper_bot/utils/__pycache__/base_dependency_factory.cpython-312.pyc b/helper_bot/utils/__pycache__/base_dependency_factory.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38de5d5fff50dd21ed3360350987359c0f6cec3f GIT binary patch literal 1802 zcmcIkK};K05S_of27}k3m4oesQl*w6;v#Ts)Fw)zR&7m4)Zkcu3dic4~eRQ1qfq+a5<64uf~r1sKV1BXgE_0O&slGI4;p(FXv z%%7PzGu}V*b1>KqjK=QYofQOt-%#<0=??9`$5;&%P>2Rumy;}UbS}$rFa`>@1PZ^+ zxdB<;B5~mlRMI*3$~{R-*^e@z1}Y#*mQ%BM3Kkz1?A}jhLrtj_wXCS+oOMag znL4#{UO1w3upiHU`(Lx9H8Ancp<=5DPe)$#y{x&ii3kq0i%`f3W>Yw?rA1o2hZTB{FtN4V->3(up9#B zFnmn%{mG*r_pl=H@LpE+o%z9LgYrMCE)qh(yJ z!?oE0Rb|EYdx=oWnFYOU*xh;6oY8enl^Gn9*+>b7s?FK~H&k-cb+-I%{-$afIPIqx zy_iUypUzmhIhmF+R4+12W76He5_iAMPSWnneY^owhOmB5ZaTK7jc`hM}BZbnZsL$Dd?tKC|^wG$bv zM+O^_p*`s8%8=!;=IMdu)Y|yU_{Kn8jJ3Qr&oZYy=nQ7a9sXG;yfOSVbf%g7@X_GI z!JXvgdh+t)$didL8p)|UsoMDZ_~t>z0r+JcW-Rwwx+%<{i}#)F6^Vk z!^BSmk4GBGYp-KI@4-Tz`>^1Be3!56Kin72{yWKp8qPDiyhf7{C+Rq$Z?b#Fa9#}z zJVkK^ZIL|<$$LHvvG!l)WglGI2JW=m(Ci7VEv_s+@xMj;*hM<-!;f5+OR6N<-I7$& zm3a+iQIfuzmo?Wyv0B>Cj_<*Va|)sO_M8KErx9ew66|q3KGt}Q`1)N>bczcUtrT}L x#X{WBcTmDI9Jm+$i*ELhXpG%q!v7@|#TM3X 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 Rnb*q^egCQF-}Y^b{{ycn`?UZ7 literal 0 HcmV?d00001 diff --git a/helper_bot/utils/__pycache__/state.cpython-312.pyc b/helper_bot/utils/__pycache__/state.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8227ac43e58d187f7cc24eebced1f126e4eb5f14 GIT binary patch literal 673 zcmY+C%}OId5XY3?wK5>4^{tPS5iVqTVMVTT79iqD+diDaxfNpQ1vFiYY1$5$`-Nn>`E(wW?*7!}yQm z6O35_V?S^Qp3o(X{|+24j!=y^%-`5C4mvE`J~YinyTf#&{<~>0ZFk@3uw3h)fu+)S z3=4l_(tVHJ2Fr`hy{2XSx`UZ`{o8hZtJ@m(P95sEXmCz?QP_=$M^32Yub&dHPw4tl z5OrgTg&ag@Uf2!A*lYbE&G2KS5#5CbH)EKVmL}$?G5u7ToQ%vl6&EL)qxy7Vd2%t@ zpKm6aiCbK`$zC?;^88Gzs<9>KED-I~jzhmDuchKA$Z5p4R|LwM6|UZLKjQiQq@OwR a9|xfbzNiSHNBH_blSf~!i?6_)toi_-5T8B( literal 0 HcmV?d00001 diff --git a/helper_bot/utils/base_dependency_factory.py b/helper_bot/utils/base_dependency_factory.py new file mode 100644 index 0000000..283d00d --- /dev/null +++ b/helper_bot/utils/base_dependency_factory.py @@ -0,0 +1,25 @@ +import configparser +import os +import sys + + +class BaseDependencyFactory: + def __init__(self): + # Загрузка настроек из settings.ini + config_path = os.path.join(sys.path[0], 'settings.ini') + self.config = configparser.ConfigParser() + self.config.read(config_path) + self.settings = {} + for section in self.config.sections(): + self.settings[section] = {} + for key in self.config[section]: + # Преобразование значений в соответствующий тип + if key == 'PREVIEW_LINK': + self.settings[section][key] = self.config.getboolean(section, key) + elif key == 'LOGS' or key == 'TEST': + self.settings[section][key] = self.config.getboolean(section, key) + else: + self.settings[section][key] = self.config.get(section, key) + + def get_settings(self): + return self.settings diff --git a/helper_bot/utils/helper_func.py b/helper_bot/utils/helper_func.py new file mode 100644 index 0000000..deeacfc --- /dev/null +++ b/helper_bot/utils/helper_func.py @@ -0,0 +1,191 @@ +from datetime import datetime, timedelta + +from aiogram import types +from aiogram.types import InputMediaPhoto + +from helper_bot.keyboards import get_reply_keyboard_for_post +from database.db import BotDB + +BotDB = BotDB('database/tg-bot-database') + + +def get_first_name(message: types.Message) -> str: + return message.from_user.first_name + + +def get_text_message(post_text: str, first_name: str, username: str): + """ + Форматирует текст сообщения для публикации в зависимости от наличия ключевых слов "анон" и "неанон". + + Args: + post_text: Текст сообщения + first_name: Имя автора поста + username: Юзернейм автора поста + + Returns: + Кортеж из двух элементов: + - Сформированный текст сообщения. + - Флаг, указывающий, является ли пост анонимным (True - анонимный, False - не анонимный). + """ + if "неанон" in post_text or "не анон" in post_text: + is_anonymous = False + return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}', is_anonymous + elif "анон" in post_text: + is_anonymous = True + return f'Пост из ТГ:\n{post_text}\n\nПост опубликован анонимно', is_anonymous + else: + is_anonymous = False + return f'Пост из ТГ:\n{post_text}\n\nАвтор поста: {first_name} @{username}', is_anonymous + + +def process_photo_album(album, post_caption: str = ''): + """ + Создает список InputMediaPhoto для альбома. + + Args: + album: Album объект из Telegram API. + post_caption: Текст подписи к первому фото. + + Returns: + Список InputMediaPhoto. + """ + photo_media = [] + for i, message in enumerate(album): + if i == 0: + photo_media.append(InputMediaPhoto(media=message.photo[-1].file_id, caption=post_caption)) + else: + photo_media.append(InputMediaPhoto(media=message.photo[-1].file_id)) + return photo_media + + +async def send_media_group_message(chat_id: int, message: types.Message, media_group: list[InputMediaPhoto]): + sent_message = await message.bot.send_media_group( + chat_id=chat_id, + media=media_group, + ) + message_id = sent_message[-1].message_id + return message_id + + +async def send_text_message(chat_id, message: types.Message, post_text: str, markup: types.ReplyKeyboardMarkup = None): + if markup is None: + sent_message = await message.bot.send_message( + chat_id=chat_id, + text=post_text + ) + message_id = sent_message.message_id + return message_id + else: + sent_message = await message.bot.send_message( + chat_id=chat_id, + text=post_text, + reply_markup=markup + ) + message_id = sent_message.message_id + return message_id + + +async def send_photo_message(chat_id, message: types.Message, photo: str, post_text: str, markup: types.ReplyKeyboardMarkup = None): + if markup is None: + await message.bot.send_photo( + chat_id=chat_id, + caption=post_text, + photo=photo + ) + else: + await message.bot.send_photo( + chat_id=chat_id, + caption=post_text, + photo=photo, + reply_markup=markup + ) + + +def check_access(user_id: int): + """Проверка прав на совершение действий""" + return BotDB.is_admin(user_id) + + +def add_days_to_date(days: int): + """Прибавляет указанное количество дней к текущей дате и возвращает дату в формате DD-MM-YYYY.""" + current_date = datetime.now() + future_date = current_date + timedelta(days=days) + formatted_date = future_date.strftime("%d-%m-%Y") + return formatted_date + + +def get_banned_users_list(offset: int): + """ + Возвращает сообщение со списком пользователей и словарь с ником + идентификатором + + Args: + offset: отступ для запроса в базу данных + + Returns: + message - текст сообщения + user_ids - лист кортежей [(user_name: user_id)] + """ + users = BotDB.get_banned_users_from_db_with_limits(limit=7, offset=offset) + message = "Список заблокированных пользователей:\n" + + for user in users: + message += f"Пользователь: {user[0]}\n" + message += f"Причина бана: {user[2]}\n" + message += f"Дата разбана: {user[3]}\n\n" + return message + + +def get_banned_users_buttons(): + """ + Возвращает сообщение со списком пользователей и словарь с ником + идентификатором + + Args: + offset: отступ для запроса в базу данных + + Returns: + message - текст сообщения + user_ids - лист кортежей [(user_name: user_id)] + """ + users = BotDB.get_banned_users_from_db() + user_ids = [] + + for user in users: + user_ids.append((user[0], user[1])) + return user_ids + + +def get_help_message_id(media_group_message_id: int, data: dict) -> int: + """ + Получает идентификатор сообщения помощи по идентификатору сообщения группы. + + Args: + media_group_message_id: Идентификатор сообщения группы + data: Словарь с данными. + + Returns: + Идентификатор сообщения помощи. + """ + + if 'help_message_id' in data and 'media_group_message_id' in data: + return data['media_group_message_id'] + else: + return 0 + + +def delete_user_blacklist(user_id: int): + return BotDB.delete_user_blacklist(user_id=user_id) + + +def unban_notifier(self): + # Получение сегодняшней даты в формате DD-MM-YYYY + current_date = datetime.now() + print('Мы в функции unban_notifier') + today = current_date.strftime("%d-%m-%Y") + # Получение списка разблокированных пользователей + unblocked_users = self.BotDB.get_users_for_unblock_today(today) + message = "Разблокированные пользователи:\n" + for user_id, user_name in unblocked_users.items(): + message += f"ID: {user_id}, Имя: {user_name}\n" + + # Отправка сообщения в канал + self.bot.send_message(self.GROUP_FOR_MESSAGE, message) \ No newline at end of file diff --git a/messages.py b/helper_bot/utils/messages.py similarity index 100% rename from messages.py rename to helper_bot/utils/messages.py diff --git a/helper_bot/utils/state.py b/helper_bot/utils/state.py new file mode 100644 index 0000000..6d4e320 --- /dev/null +++ b/helper_bot/utils/state.py @@ -0,0 +1,13 @@ +from aiogram.fsm.state import StatesGroup, State + + +class StateUser(StatesGroup): + START = State() + SUGGEST = State() + ADMIN = State() + CHAT = State() + PRE_CHAT = State() + BAN_2 = State() + BAN_3 = State() + BAN_4 = State() + BAN_FINAL = State() diff --git a/main.py b/main.py deleted file mode 100644 index 24eba19..0000000 --- a/main.py +++ /dev/null @@ -1,50 +0,0 @@ -import configparser -import os -import sys -from database.db import BotDB -from helper_bot.helper_bot import TelegramHelperBot -from logs.custom_logger import Logger - - -#TODO: Добавить проверку можно ли отвечать пользователю? Сейчас если у него скрыто лс, ему похоже не приходят сообщения -#TODO Подумать над реализацией функционала с поступлениями в колледжи -#TODO: Покрыть все логированием и ошибками корректными. Ерроры кидать в чат. -#TODO: Покрыть все тестами - - -class BaseDependencyFactory: - def __init__(self): - # Загрузка настроек из settings.ini - self.logger = Logger('main') - config_path = os.path.join(sys.path[0], 'settings.ini') - self.config = configparser.ConfigParser() - self.config.read(config_path) - self.BotDB = BotDB('database/tg-bot-database') - - self.settings = {} - for section in self.config.sections(): - self.settings[section] = {} - for key in self.config[section]: - # Преобразование значений в соответствующий тип - if key == 'PREVIEW_LINK': - self.settings[section][key] = self.config.getboolean(section, key) - elif key == 'LOGS' or key == 'TEST': - self.settings[section][key] = self.config.getboolean(section, key) - else: - self.settings[section][key] = self.config.get(section, key) - - def get_settings(self): - return self.settings - - def get_database(self): - return self.BotDB - - -if __name__ == "__main__": - # Запускаем тг бота - bot = TelegramHelperBot(BaseDependencyFactory()) - bot.start() - - #scheduler = BackgroundScheduler() - #scheduler.add_job(bot.unban_notifier(), 'cron', hour=22, minute=9) - #scheduler.start() diff --git a/requirements.txt b/requirements.txt index 0715972..e07318e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ APScheduler==3.10.4 -certifi==2024.7.4 +certifi~=2024.6.2 charset-normalizer==3.3.2 coverage==7.5.4 exceptiongroup==1.2.1 @@ -8,11 +8,15 @@ iniconfig==2.0.0 loguru==0.7.2 packaging==24.1 pluggy==1.5.0 -pyTelegramBotAPI==4.20.0 pytest==8.2.2 pytz==2024.1 requests==2.32.3 six==1.16.0 tomli==2.0.1 tzlocal==5.2 -urllib3==2.2.2 +urllib3~=2.2.1 +pip~=23.2.1 +attrs~=23.2.0 +typing_extensions~=4.12.2 +aiohttp~=3.9.5 +aiogram~=3.10.0 \ No newline at end of file diff --git a/run_helper.py b/run_helper.py new file mode 100644 index 0000000..ad7b9df --- /dev/null +++ b/run_helper.py @@ -0,0 +1,7 @@ +import asyncio +from helper_bot.main import start_bot +from helper_bot.utils.base_dependency_factory import BaseDependencyFactory + + +if __name__ == '__main__': + asyncio.run(start_bot(BaseDependencyFactory()))