From 4e8f5e71a787bb219c56c95634bb044dd0878d9a Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 23 Sep 2024 16:48:32 +0300 Subject: [PATCH 1/8] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/__pycache__/keys.cpython-310.pyc | Bin 9019 -> 9121 bytes handlers/keys.py | 12 +++++++++++- key_management.py | 6 +++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 9a69ce573cffec6e192a71f37457c840e8cfede1..0ccaa853fc7414eef06dfe9e94f8e936a898d31a 100644 GIT binary patch delta 962 zcmZvaO-vI(6vy{%KWw`nv}Ko;t&o-|P`MD|SE3;v{AeW+lnQb{tWK$|Qp&PKC0oIG z?&%p4Lr7Y~(Tn!r!J~=ZIB@VlV$yIhCSLSFVvLE-D_}` z(!UCXb^0m-4Kc#Ih*93#kt>$N`336SIaCRLRyRP-Gm^K~2vbhf^+oE4Ylu0m2^ zLP97x7T57fK!?}3t@kYDEh#UvL(F4~tHPSdyk)WEX=pwsHD+EuQ*2KbY;P8_B9qHd z@>y!Wg()`JLf)Sb*gmF~oxAx#7#D0uC0G@jb0&-ml%0@E!|MYG+^9TBQf(OFv+xEfk>m1Ht$%Lhz`S$o;lw8d=( z4)`p1$T!y8^fRA9C@CN;wl(@RY=zEdKM{+R9nB;Y{8UTIW2NBQePe$p1cxyZN=3{o z^nZp45yA+eo6w2RLt_xdZ=r;^Fo0=E$5i+vJ`1n7uhRN8>~BAD-pugtc-ZVAsh8j< z{Pz;jf=C$W*z#JrnasSEo+;vx7B>`H7$Zy&1_;MF;wmm_o#-+&?=8{>IWTM__|;1r z5ls_S(7-8*AcC6@`IqR%Wg`xA{H`6r4Wk=2Ydgla7|T<1h>#>)Cfp-5Iso%V?MKH? zh?DazN3LM~bBAf~U;AbOBhi~Mjf>G_$1KIP6fzH{G7pP6^U)Nww&lDtJ8zjqtVg3# zmV)mR&SFdr<4b=~GBXrWyq_*?;<;EFmhnaG0%Y(@Y!H0d)3vUrDS#1jggL@8*1N`B J13{2a{Q*Nh;m`m8 delta 867 zcmZva%WD%+6vpqFJUYo_(wQXFOcN+cingh?l!Azxy3y8Dh<({Er0?6thBhhFDPo!Q zfg3kj9VtktBp{+o$3l>$? z&&1WM{#tl(?QQU*0&DnLX%{?HcDzGkY3(j*iDvxa3DrLYz5+~Pw{Z!wsOZW1&)@{W zFlMw0%pS?pbwyWP#;|zKN)tK=0m4ZAO2iG26TXQcDfHs^qE=typxzR$*zT&w_SPW7 zIJ?72uJxz5tsAg|2S(dw)>iTX-r#`4@@hWF<+AJ82W*@1gPvqf}Ip z|8!$6<3y-ckDg6T^QKMQijLy%=!kbteAKmWoKXy%HoF?EIQ2azy&K<{Jq^}np;;~X z#eA;ZquW99bc&NU@maHZGi_RJB)1bZ!hesH79_%bfqkF7xIDjV&n&OtVRJ0zp@lBO zRf0+wC5+=rq!Wi?5iG{~6SssUZOM`b5*38NEy*B*2C@_rJ${d+p@>?10QTw+;s;Xl zA#D;RWC#O}wtKw1*&Y{VyTSz}f%+ diff --git a/handlers/keys.py b/handlers/keys.py index 424da168..d7029ace 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -172,7 +172,17 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): current_time = datetime.utcnow().timestamp() * 1000 if expiry_time <= current_time: - await bot.edit_message_text("Ваш ключ уже истек и не может быть продлен.", chat_id=tg_id, message_id=callback_query.message.message_id) + # Кнопка для удаления ключа и возврата в профиль + delete_button = types.InlineKeyboardButton(text='Удалить ключ', callback_data=f'delete_key_{client_id}') + back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[delete_button], [back_button]]) + + await bot.edit_message_text( + "Ваш ключ уже истек и не может быть продлен.", + chat_id=tg_id, + message_id=callback_query.message.message_id, + reply_markup=keyboard + ) return keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ diff --git a/key_management.py b/key_management.py index 44feb0bf..9d1da1b6 100644 --- a/key_management.py +++ b/key_management.py @@ -106,7 +106,11 @@ async def handle_text(message: Message, state: FSMContext): if message.text in ["/start", "Меню"]: await start_command(message) return - + + if message.text.lower() == "карина кринж": + await message.answer("Это и так всем понятно, но лучше займемся доступом к впн. Кстати, с днем рождения!") + return + if current_state == Form.waiting_for_key_name.state: await handle_key_name_input(message, state) From be351807037a8014450f5d1160159f75c296a905 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 23 Sep 2024 18:54:16 +0300 Subject: [PATCH 2/8] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- key_management.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/key_management.py b/key_management.py index 9d1da1b6..dec49dd7 100644 --- a/key_management.py +++ b/key_management.py @@ -250,3 +250,25 @@ async def renew_expired_keys(): await asyncio.sleep(3600) + +async def notify_expiring_keys(): + while True: + current_time = datetime.utcnow() + threshold_time = int((current_time + timedelta(hours=10)).timestamp() * 1000) + + conn = await asyncpg.connect(DATABASE_URL) + try: + # Получаем пользователей с истекающими ключами + expiring_keys = await conn.fetch('SELECT tg_id, expiry_time FROM keys WHERE expiry_time <= $1', threshold_time) + + for record in expiring_keys: + tg_id = record['tg_id'] + expiry_time = record['expiry_time'] + + # Отправляем уведомление пользователю + await bot.send_message(tg_id, f"🔔 Ваш ключ истекает через 10 часов. Пожалуйста, продлите его, чтобы избежать отключения.") + + finally: + await conn.close() + + await asyncio.sleep(3600) # Проверяем каждый час \ No newline at end of file From 3fa93a9b728f68b5ba530256cbf4e718f5899103 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 23 Sep 2024 19:08:41 +0300 Subject: [PATCH 3/8] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/start.py | 13 +++++++++++-- key_management.py | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/handlers/start.py b/handlers/start.py index 6aa6d43c..a586b365 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -9,6 +9,7 @@ import os from bot import bot from config import ADMIN_ID, CHANNEL_URL +from aiogram.types import ReplyKeyboardMarkup, KeyboardButton router = Router() @@ -31,7 +32,12 @@ async def send_welcome_message(chat_id: int): await bot.send_message(chat_id, "Файл изображения не найден.") return - # Создаем клавиатуру + # Создаем клавиатуру с кнопкой "Старт" + keyboard = ReplyKeyboardMarkup(resize_keyboard=True) + start_button = KeyboardButton(text='/start') + keyboard.add(start_button) + + # Создаем inline-клавиатуру inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')], [InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')], @@ -49,9 +55,12 @@ async def send_welcome_message(chat_id: int): ), caption=welcome_text, parse_mode='Markdown', - reply_markup=inline_keyboard + reply_markup=inline_keyboard # Inline-клавиатура ) + # Отправляем клавиатуру с кнопкой "Старт" + await bot.send_message(chat_id, "Нажмите /start для повторного запуска.", reply_markup=keyboard) + @router.message(Command('start')) async def start_command(message: Message): await send_welcome_message(message.chat.id) diff --git a/key_management.py b/key_management.py index dec49dd7..6fe46dd0 100644 --- a/key_management.py +++ b/key_management.py @@ -19,6 +19,8 @@ from database import (add_connection, get_balance, has_active_key, store_key, update_balance) from handlers.profile import process_callback_view_profile from handlers.start import start_command +from aiogram.types import ReplyKeyboardMarkup, KeyboardButton + router = Router() @@ -87,6 +89,12 @@ async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) await callback_query.answer() +def start_keyboard(): + keyboard = ReplyKeyboardMarkup(resize_keyboard=True) + start_button = KeyboardButton(text='/start') + keyboard.add(start_button) + return keyboard + # Обработка текстовых сообщений @dp.message() async def handle_text(message: Message, state: FSMContext): @@ -114,6 +122,8 @@ async def handle_text(message: Message, state: FSMContext): if current_state == Form.waiting_for_key_name.state: await handle_key_name_input(message, state) + await message.answer("Выберите действие:", reply_markup=start_keyboard()) + async def handle_key_name_input(message: Message, state: FSMContext): tg_id = message.from_user.id From f83278af90d356a4a773fd7fb3f3e3525faa033b Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 24 Sep 2024 02:35:54 +0300 Subject: [PATCH 4/8] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/__pycache__/start.cpython-310.pyc | Bin 4878 -> 5162 bytes handlers/pic.jpg | Bin 0 -> 33288 bytes handlers/start.py | 37 ++++++++++++--------- 3 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 handlers/pic.jpg diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index c844f7d6f4ee913bf97a75d68e89e97743f80b17..79afa263ebd9ff04da97057659c8e2f8ea1e89f3 100644 GIT binary patch delta 1763 zcmaJ>OKclO7~a`kuixueVmr;d1&UiliNr%KQJR)MX$gG5{+*XQt8e@HBnZ~KUq!u@@+~t0z(nOnF zAvDQ!kfdmONo|wj0LTVue}?ufNg(WHQ4nTn&KC}$Q0OEV81}g>q(4038_Um+iYbyK z8%3EMCYo3wdmi>O$5_33`Vc!kW1F15VDgjAx~Jb??`}GdT@$~NTUrhZz;ogYd0Qq8 zY!YTe1rm}Xgr~(_xla%JOG)|~WxtD!%EF=(qdHA2lUW!vNmI-6g6hOuGEKMT3;hcL zN1NAMl9Ol!E+?-@6jEB2TaZ#qq1olYL8$BjX+e7#{wj>NLZn(~Olr)(L5PIo6_{Wn z*pg_jrPBPe(%pkKKQ0wWpZHbzD;TU-N{`Ri#Xa?h*9RatPigY%RpKQowQ8ltjFYSB zH$xzF-*d0Kw>#$xgrn|sLH0uT6}$vzjmnvScX%?NITLHxRog%x_1dgZv}a#x?nguK zyVu;$-CORB&N=t`hw;g>v z;0<@h{ar@Mwk)$o_n>P$ZSvWy%y)vR0r7J%^UM%H&_|V;jtPHKT5l#aUNx;5YEB#g z&0l075sB|z84pd#L;bMgqewObxhnGl@_wxY5bj5j5wc%=6M1TMU1-TUh^Wd8bH;8u z#;JPkfEbRBk|Pk+;1iQzqaaW5gJ^3PlIM`DHNp1+_8Q{*{HlByotztoWkKuK;t-1b zGKKxlG#iZb2?P(L7U6ij*4gwjt6|NUrIUtZ8y2fI(MJOhwR|?Iq?I(`$JRtSjCTGv z%30Ldw{j`=Dp|xDb`x7$I{rKe3d9RyoCdTs`al|C+=S1kAG;TBFvo!2I*j8p!m~iU zXtNF-WCpg8A4M3GEd;uG9!HgUnLW*DH#hdC!>3T|MI>uW>m^ppP-b2AS@DU!iJTA} z{aZ4<@!cCy$em4 z`K>;J+%Y64kYLd`x_TgazYKZ6ywJF1)28??-Sf~0i76oxlaoY~ld`VF#*%=*?1dTX JA(6}m@;6WryCeVr delta 1614 zcmaJ>OK;p%6!yJ#JTK29^U9->7i|h7%A*KUkwj4&O4HIbQ51=X1#&!d?=+K+?a{TT z1u0d65|CIF6kV}IN|!}qfwH4WU9g59Fi31bELgB%!=h?A=T0h`AYto#K0fF8Jn!+> z&wYM66Qt8g4X(9;KO3Kv_A(VRyZ8KxO*E!4vz7F+c2@DRR?g0WEZ*w(`t5!tOSJM{ z!7g}3yGS%nb~L-Rps^%N?P@H=6Fa(HW)>f4P?lzyUBe#arTwA3Uk0{@fjPknthlS) zC9K3#U{YoSyT%T&D@r$bL^s5Sm2O1oPW-E56>c^RHo``geiQ@n^2Qi=&2hSsIn3C{NMv%@UL^O7eI5(sT~kEKq0w#I+<5DCx%P>u%&US)IV! zldBJyIM5G3;ZJf|zy0mKQ5n|`b5bLR&Cs1~?_b*ZSyFU=WR&C1ExnfWx@S|RtDQq`e+Qfv}WVaF=0Ov_0zkK^P9K+JmYydDp}IwScl@Chhj+M5Amb4=kk9=6%wzE?dhH9-kt?EZ z?63m45ucq`8Uv@(rA<~PVhT);=aUZcLkF_)T%Vo8L~y)xyG+HjTuWD^ktm$2fCyS? zFgJ4HN$SVl#AjQsS7Ywn3|Rg}0g{?Hf3TETqN79bCdLp>0^Flw68X~r(7Y-6U22@n z%b!!PO&n`gZXGgeamTF%oyfV__Gjc-Yl2*btVUkOI90kNE~2klggJzxF2s3|t)TpZ z+_&Cc$MULXuMd|{tXh;%Xq5$USchG-`XPs{6 z^gIksTsxBC2>SWo3~jWyaPUp~1G4o7@~Wt#Z4+;TqDs0k%oDb)ntG}XbyU@%#oW7z zkVg*8HR5VRMG=LmP8+7j9UP%p#3)!SOhDDSgeK_*pW>`n8~-p8SJ3J$gkx>U9=4&u zd*-%$kUdFO<*(Tv$@0Osxlc(6H&Ebei5Czu2n%vCKUP+!P{m8AT2w;I^5gu_e-41) zb>6%Q`*aw`m-q8k^L_9I|EWLnpKU#hZq)*t^}ug5ulE9}tu+N48VW<%mghG^1OS QIb!KRC7^hbBn!jj9}i7tjQ{`u diff --git a/handlers/pic.jpg b/handlers/pic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cced6e0380b744c87689c87bec6c42e15a0a9625 GIT binary patch literal 33288 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 Date: Tue, 24 Sep 2024 02:36:30 +0300 Subject: [PATCH 5/8] =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/start.py b/handlers/start.py index 92fbbd8c..72e174ed 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -19,7 +19,7 @@ class FeedbackState(StatesGroup): async def send_welcome_message(chat_id: int): # Новый текст приветствия welcome_text = ( - "*SoloNet -- ваш провайдер в бесцензурный интернет!*\n\n" + "*SoloNet — ваш провайдер в бесцензурный интернет!*\n\n" "Получите высокую скорость и самый безопасный протокол VPN, " "который работает даже в Китае." ) From 146b61a1a8f3927418959adddadfaff16b59525c Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 25 Sep 2024 01:45:37 +0300 Subject: [PATCH 6/8] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B2=20=D0=B1=D0=B0=D0=B7=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/__pycache__/keys.cpython-310.pyc | Bin 9121 -> 7812 bytes handlers/__pycache__/pay.cpython-310.pyc | Bin 8628 -> 7951 bytes handlers/__pycache__/start.cpython-310.pyc | Bin 5162 -> 5163 bytes handlers/keys.py | 106 ++++++--------------- handlers/pay.py | 17 +--- key_management.py | 41 +------- 6 files changed, 36 insertions(+), 128 deletions(-) diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 0ccaa853fc7414eef06dfe9e94f8e936a898d31a..3ae3ac58bda8432413e0623a515753eb74fe28d6 100644 GIT binary patch delta 1273 zcmaKrUuauZ9LLY^li=vWfYPM!d@gL4rEgV9~2*idwmco_@WQ8A^5Pj`k=qFw&)N{_$twDdIUHKFIK#A`2Mk+V~;oYEV6!)WXJ@j%{DM72w z$IfR?-MQy{?p}4@)1AA{J@zsmiW-~1_aa$2BBEI3Adla3yh( zmwIt5)QXpr*|u_I@2MAN$BOm|+f2)+dwPG_i?@@R^kX3>ZL^a?%p($6{3&^Qe2`q- zU2)f(?Vi4;#`}ZLmUG9s>#n)$PTjqs%dT;+I(3~IXO)~g?sZ!Kl4i8F-$YLRl-@Vq zZw^zgvR7s?&&nOn5lY?ENlE;mJvn)sn>i`x35BHjr1XzUXc8k51&J<+u*3xkF7XOs zCtyv_8H?7~+=A(oVvodeiHO8SiA%Vucc2w7;Q3S>=Tk4_Y?2rI2(ouI;L;!lUao)w z8hAbUAk_&|)R~7P<wZ>x;}Y_JWe!QaL@43I#E3FM zUIENk@y+ZA9Kr4EkBKNry7L8cvzI(LWsTX?anKbZpa%q^vf!q_g8Bby<*9%+tN$yaNbBO0szi6P+ zg;n~-AtSYT@DI8?k8ymE>wqOx@-M;!p2_#ef2MORdI@|b3+7333K^{CZ)&6Dnqv}E k5=%I6=-6@jf*O;R4f8P7Gfw8`VNVRgJ^QB8*hzNkZ@>&w6951J delta 2206 zcma)7U2IfE6uvY2clYn^?d@)VcDLK56lB{{Y(YUNC4_$=fNixxLe{pkly2K?xqDZj zxoZn05)ul*Av|aZZ8TA$CWVTC$deB~k=*6Mhke3mOo$;tBMAv8o^y9gi|}BZ`R<&V zGiT1seBasQYp1qILtd{-fWP4IANxJ4&Prjj`f`0KX-Je#`dN^`uk+L_0W z$HY!yn|qd-C(a(?$-C4^YJZ*XvehtrW60P1Ky4Yn*27np?N)6nYNb zp|-**53|j!A_B8##gvUX04Wo*qUKFX%*kBLJwdZhutt5lvzPP|1}DzCG&xzKi#pMT zq>?PvUCcXSEhIpMP65LEr-WHMB|dK-0*_RURnP)Q&JiK&Cc;P=#$%H1{zSMyXQMLT zgk?5Hr@%**^18CQs7!#@mv^c`xOWvVf26*B(~eCWI--feuD$_&Td5UC7Hx!~j!j@x zJ2DO6WPHqb2djL!@#0%wHEuO#Ho#MsH_ab2t{c})20#Z~a>tD6Xzqk@H8%$4wdnfY z+Zr>kb0b)_G726*nJBFO*0_qF0*NrNx(IXgA%qYj2vLAc6Ygj)ICEd9yzLTI7vO9S zK&)JS1^Y`7WP}cc7(yL_2jOi5JAxYlb)<>_c}L=Kq9>#AI`1<6$X{EYccc^Pbl*@4 zI+>mx=&LQ@$Deqs_f-$8LpT!e=^7a5?&|4}KTprY;FxX(KxWH?Vj~K%Qjxf*3%&}m zgSM;|31%X+7QPKc9HUPai`s`o=D*7e$wj2UBtaO=@&}B07b)EUl)m-?r9XQqrLXWK zCDBpfu%n--NzI+~Fb5NLiiBZK;HYSh0bfna36@tEdx=R}S|qKM^f9kVFQl)FN&g|O zGDojYP*}TwQOvkLCc|E%#m#z&IatD!xde)1rG*4QDVE}f0&iIaZa z&g>zmaFYR6Hbr2x{DLj((I83PGcU;sRykoS=%At~u6cQy;!^lJC9vi<5R=yw6P`1u zd!LC3#TA+_raf@FtoH*iDBk??GfFuL^UsxzYIP^PkGc!NB<*hOEk;24KIBg;+DMc? zQdTK>sZd6n;)6pe?La)!%>Py@dAu}Sn)el^mKUbdadi^a0K_EzkH6XZ9@?z{ zd|6<_8yBIIMt#1h5Ru44q<$(9KPZKQa_rDNPpI85TmR%du!tN)jfi=B;kNh#IYREB zSY{$+w`kKnf?I6?xM!A*B%1hBrEWub6QLi$JULO{<$EgIh>w3&`BMO^PSp4w?(Asp zOzy)aOJj}jJ`aK`ERC5}e5)n;m}j@7rg&FqpH1Bl?)=Bl zW-`R3aEJ`^x^T5i?Zq?>;MCecke`+nci~9G16`>*{G;%~b#QclB>1aLBq$yd{QF55 zF2%379>``P@xygV&>}D%q;#y-BF`0*G5%M06G`yZRjqy(Tua&td(p87QI|s}R>eXDnw3pL|V1kr7PF)-Wy*U&!Fekiy`> zz{pU;us{OAE8qN0;twNhQ3}wZo4ushF*2r3{wY% ZBn4t9OpZ}_KRHfOiBV^AqoOWQI{>8NN!tJb delta 898 zcmZ`$T}YEr7(VaW=9bNA)8?jAzo{uB)GmZF#Ii0dq7g_0b}`Je-*ndbHpG~2Q^BH^ z=pl$A(#s&EoRVeEi=eBL^WE+ugzoJ&h<@I0<%LKG-simMocHHB&pX$%dfql#R#r-I z^_1-fUv|vf+%PB%*?egIAM>+M9h zz0$2{8AVIWHTh1i$aVQfu4_~Bm3%MPPb;~G+GPMHw%tj@OLetCMX0XRWwOv3u!AvO zK^3S0OPc<&8STKQ7aZ=CYDYDaBuYjtIxYf(CH<$=m*~Y8`3!rl%Dk4*`=I5uS-C0m z$fHPOwfL~2V9bU#r^qe5&FcqD+eC76UdrpS4!?rng#wIlr+zGgi;xuIop|0wQz?R- z@N>8g0m9xgpkqe+fTngMWI(4Hg$an$xPbwq%#fua2mv!@VS>huD1a$R8DS;NoHgyq z&Io?Y;!8AO4fz~-Zpl<0sp>?hvOLQ@(VZr-UH7eApIpIw`7b*@E8f<( QzzMNa-&W!PYo7!D0Dj9s4gdfE diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index 79afa263ebd9ff04da97057659c8e2f8ea1e89f3..4348070429f3d0639335c2620e25d21808442ede 100644 GIT binary patch delta 44 xcmZ3bv08&SpO=@50SGF7e@qMA$m_@=@B_%x3eL~T_e(8Nc+@aub1chQegGx!4rBlT delta 43 wcmZ3ju}XtCpO=@50SN4Ve@qM6$m__${~gHF3eL~T_e(8N(AC`>$8we*00^lJi~s-t diff --git a/handlers/keys.py b/handlers/keys.py index d7029ace..af9e448f 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -29,7 +29,8 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): for record in records: key_name = record['email'] client_id = record['client_id'] - button = types.InlineKeyboardButton(text=key_name, callback_data=f'view_key_{key_name}_{client_id}') + # Заменяем подчеркивание на вертикальную черту в callback_data + button = types.InlineKeyboardButton(text=key_name, callback_data=f'view_key|{key_name}|{client_id}') buttons.append([button]) # Создаем клавиатуру с кнопками @@ -58,10 +59,11 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): await callback_query.answer() -@router.callback_query(lambda c: c.data.startswith('view_key_')) +@router.callback_query(lambda c: c.data.startswith('view_key|')) async def process_callback_view_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id - key_name, client_id = callback_query.data.split('_')[2], callback_query.data.split('_')[3] + # Разделяем данные по вертикальной черте + key_name, client_id = callback_query.data.split('|')[1], callback_query.data.split('|')[2] try: conn = await asyncpg.connect(DATABASE_URL) @@ -85,9 +87,9 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): f"{days_left_message}") # Кнопки для продления, инструкций и удаления - renew_button = types.InlineKeyboardButton(text='Продлить ключ', callback_data=f'renew_key_{client_id}') + renew_button = types.InlineKeyboardButton(text='Продлить ключ', callback_data=f'renew_key|{client_id}') instructions_button = types.InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions') - delete_button = types.InlineKeyboardButton(text='Удалить ключ', callback_data=f'delete_key_{client_id}') + delete_button = types.InlineKeyboardButton(text='Удалить ключ', callback_data=f'delete_key|{client_id}') back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile') keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[renew_button], [instructions_button], [delete_button], [back_button]]) @@ -104,62 +106,25 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): await callback_query.answer() # Обработка запроса на удаление ключа -@router.callback_query(lambda c: c.data.startswith('delete_key_')) +@router.callback_query(lambda c: c.data.startswith('delete_key|')) async def process_callback_delete_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id - client_id = callback_query.data.split('_')[2] + client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта confirmation_keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ - [types.InlineKeyboardButton(text='Да, удалить', callback_data=f'confirm_delete_{client_id}')], + [types.InlineKeyboardButton(text='Да, удалить', callback_data=f'confirm_delete|{client_id}')], [types.InlineKeyboardButton(text='Нет, отменить', callback_data='view_keys')] ]) await bot.edit_message_text("Вы уверены, что хотите удалить ключ?", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=confirmation_keyboard) await callback_query.answer() -@router.callback_query(lambda c: c.data.startswith('confirm_delete_')) -async def process_callback_confirm_delete(callback_query: types.CallbackQuery): - tg_id = callback_query.from_user.id - client_id = callback_query.data.split('_')[2] - try: - conn = await asyncpg.connect(DATABASE_URL) - try: - record = await conn.fetchrow('SELECT email FROM keys WHERE client_id = $1', client_id) - - if record: - email = record['email'] - - session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) - success = delete_client(session, client_id) - - if success: - await conn.execute('DELETE FROM keys WHERE client_id = $1', client_id) - response_message = "Ключ был успешно удален." - else: - response_message = "Ошибка при удалении клиента через API." - - else: - response_message = "Ключ не найден или уже удален." - - back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) - - await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) - - finally: - await conn.close() - - except Exception as e: - await bot.edit_message_text(f"Ошибка при удалении ключа: {e}", chat_id=tg_id, message_id=callback_query.message.message_id) - - await callback_query.answer() - - -@router.callback_query(lambda c: c.data.startswith('renew_key_')) +# Подтверждение удаления +@router.callback_query(lambda c: c.data.startswith('renew_key|')) async def process_callback_renew_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id - client_id = callback_query.data.split('_')[2] + client_id = callback_query.data.split('|')[1] # Используем разделитель вертикальная черта try: conn = await asyncpg.connect(DATABASE_URL) @@ -169,32 +134,19 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): if record: email = record['email'] expiry_time = record['expiry_time'] - current_time = datetime.utcnow().timestamp() * 1000 - - if expiry_time <= current_time: - # Кнопка для удаления ключа и возврата в профиль - delete_button = types.InlineKeyboardButton(text='Удалить ключ', callback_data=f'delete_key_{client_id}') - back_button = types.InlineKeyboardButton(text='Назад в профиль', callback_data='view_profile') - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[delete_button], [back_button]]) - - await bot.edit_message_text( - "Ваш ключ уже истек и не может быть продлен.", - chat_id=tg_id, - message_id=callback_query.message.message_id, - reply_markup=keyboard - ) - return + current_time = datetime.utcnow().timestamp() * 1000 # Получаем текущее время в миллисекундах + # Убираем проверку на истекший ключ keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ - [types.InlineKeyboardButton(text='Продлить на 1 месяц (100 руб.)', callback_data=f'renew_1_month_{client_id}')], - [types.InlineKeyboardButton(text='Продлить на 3 месяца (250 руб.)', callback_data=f'renew_3_months_{client_id}')], + [types.InlineKeyboardButton(text='Продлить на 1 месяц (100 руб.)', callback_data=f'renew_plan|1|{client_id}')], + [types.InlineKeyboardButton(text='Продлить на 3 месяца (250 руб.)', callback_data=f'renew_plan|3|{client_id}')], [types.InlineKeyboardButton(text='Назад', callback_data='view_profile')] ]) balance = await get_balance(tg_id) response_message = (f"Выберите план продления:\n" f"Баланс: {balance} руб.\n" - f"Действующий ключ истекает {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}") + f"Текущая дата истечения ключа: {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}") await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard, parse_mode="HTML") @@ -207,10 +159,10 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): await callback_query.answer() # Обработка выбора плана продления -@router.callback_query(lambda c: c.data.startswith('renew_')) +@router.callback_query(lambda c: c.data.startswith('renew_plan|')) async def process_callback_renew_plan(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id - plan, client_id = callback_query.data.split('_')[1], callback_query.data.split('_')[3] # '1' или '3' и client_id + plan, client_id = callback_query.data.split('|')[1], callback_query.data.split('|')[2] # '1' или '3' и client_id days_to_extend = 30 * int(plan) try: @@ -221,38 +173,42 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): if record: email = record['email'] expiry_time = record['expiry_time'] - current_time = datetime.utcnow().timestamp() * 1000 + current_time = datetime.utcnow().timestamp() * 1000 # Текущее время в миллисекундах if expiry_time <= current_time: + # Если ключ уже истек, он не может быть продлен await bot.edit_message_text("Ваш ключ уже истек и не может быть продлен.", chat_id=tg_id, message_id=callback_query.message.message_id) return + # Рассчитываем новую дату истечения new_expiry_time = int(expiry_time + timedelta(days=days_to_extend).total_seconds() * 1000) cost = 100 if plan == '1' else 250 balance = await get_balance(tg_id) if balance < cost: replenish_button = types.InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') - back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') + back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_profile') keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [back_button]]) await bot.edit_message_text("Недостаточно средств для продления ключа.", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) return + # Продлеваем ключ через API session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) success = extend_client_key(session, tg_id, client_id, email, new_expiry_time) if success: + # Обновляем баланс и ключ в базе данных await update_balance(tg_id, -cost) await conn.execute('UPDATE keys SET expiry_time = $1 WHERE client_id = $2', new_expiry_time, client_id) - response_message = f"Ваш ключ был успешно продлен на {days_to_extend // 30} месяц(-)." - back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') + response_message = f"Ваш ключ был успешно продлен на {days_to_extend // 30} месяц(-а)." + back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_profile') keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) else: await bot.edit_message_text("Ошибка при продлении ключа.", chat_id=tg_id, message_id=callback_query.message.message_id) else: - await bot.edit_message_text("У вас нет ключей для продления.", chat_id=tg_id, message_id=callback_query.message.message_id) + await bot.edit_message_text("Ключ не найден.", chat_id=tg_id, message_id=callback_query.message.message_id) finally: await conn.close() @@ -262,7 +218,5 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): await callback_query.answer() -# Обработка ошибок async def handle_error(tg_id, callback_query, message): - await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id) - + await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id) \ No newline at end of file diff --git a/handlers/pay.py b/handlers/pay.py index 5c31cdb1..1828e21f 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -217,10 +217,9 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: try: await bot.delete_message(chat_id=user_id, message_id=requisites_message_id) except Exception as e: - print(f"Ошибка при удалении сообщения с реквизитами: {e}") + print(f"Ошибка при удалении сообщения: {e}") finally: - # Закрываем соединение с базой данных await conn.close() elif action == 'decline': @@ -231,14 +230,6 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: await send_message_with_deletion(callback_query.from_user.id, f"Ошибка при пополнении баланса: {e}", state=state, message_key='admin_error_message_id') print(f"Ошибка при пополнении баланса: {e}") - await state.clear() - await callback_query.answer() - - -@router.message(lambda m: m.text and m.text.startswith('Недостаточно средств для продления')) -async def handle_insufficient_funds(message: types.Message): - user_id = message.from_user.id - replenish_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance')] - ]) - await send_message_with_deletion(user_id, "Ваш баланс недостаточен для продления ключа. Пожалуйста, пополните баланс.", reply_markup=replenish_keyboard, state=None, message_key='insufficient_funds_message_id') + finally: + await state.clear() + await callback_query.answer() diff --git a/key_management.py b/key_management.py index 6fe46dd0..0417b7de 100644 --- a/key_management.py +++ b/key_management.py @@ -19,8 +19,6 @@ from database import (add_connection, get_balance, has_active_key, store_key, update_balance) from handlers.profile import process_callback_view_profile from handlers.start import start_command -from aiogram.types import ReplyKeyboardMarkup, KeyboardButton - router = Router() @@ -89,12 +87,6 @@ async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) await callback_query.answer() -def start_keyboard(): - keyboard = ReplyKeyboardMarkup(resize_keyboard=True) - start_button = KeyboardButton(text='/start') - keyboard.add(start_button) - return keyboard - # Обработка текстовых сообщений @dp.message() async def handle_text(message: Message, state: FSMContext): @@ -114,16 +106,10 @@ async def handle_text(message: Message, state: FSMContext): if message.text in ["/start", "Меню"]: await start_command(message) return - - if message.text.lower() == "карина кринж": - await message.answer("Это и так всем понятно, но лучше займемся доступом к впн. Кстати, с днем рождения!") - return - + if current_state == Form.waiting_for_key_name.state: await handle_key_name_input(message, state) - await message.answer("Выберите действие:", reply_markup=start_keyboard()) - async def handle_key_name_input(message: Message, state: FSMContext): tg_id = message.from_user.id @@ -258,27 +244,4 @@ async def renew_expired_keys(): ]) await bot.send_message(tg_id, "Ваш баланс недостаточен для продления ключа. Пожалуйста, пополните баланс.", reply_markup=replenish_keyboard) - await asyncio.sleep(3600) - - -async def notify_expiring_keys(): - while True: - current_time = datetime.utcnow() - threshold_time = int((current_time + timedelta(hours=10)).timestamp() * 1000) - - conn = await asyncpg.connect(DATABASE_URL) - try: - # Получаем пользователей с истекающими ключами - expiring_keys = await conn.fetch('SELECT tg_id, expiry_time FROM keys WHERE expiry_time <= $1', threshold_time) - - for record in expiring_keys: - tg_id = record['tg_id'] - expiry_time = record['expiry_time'] - - # Отправляем уведомление пользователю - await bot.send_message(tg_id, f"🔔 Ваш ключ истекает через 10 часов. Пожалуйста, продлите его, чтобы избежать отключения.") - - finally: - await conn.close() - - await asyncio.sleep(3600) # Проверяем каждый час \ No newline at end of file + await asyncio.sleep(3600) \ No newline at end of file From b3fb4984b6393862a5acab1ac87be3213f59626a Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 25 Sep 2024 01:51:26 +0300 Subject: [PATCH 7/8] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=B9?= =?UTF-8?q?=20=D0=B2=20=D0=B1=D0=B0=D0=B7=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/__pycache__/keys.cpython-310.pyc | Bin 7812 -> 8931 bytes handlers/keys.py | 38 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 3ae3ac58bda8432413e0623a515753eb74fe28d6..028d3eb07049c57fd5c7e2a565dd4ec08fabf4d7 100644 GIT binary patch delta 933 zcmY*XOKTHR6u#$Xl1wJeqiND;CNCuw`cP5vL9vCZ5d~4PLa`z>&7D5dn9A)0ZJ4pu z7Tj2&UU8ua%|EEbYE@j@O<}rq>r!3mLPXGW)lwa}-#O>I-}&Y+bLM+KOPY=JJLWhu%MOq(ZlV1C10ij7dFv*f%o2ZK6GvGhC{OfuR}+B1za za3Tob8;hZX0K?){EZ6uLURPjDrj%b5pTj$#E<(*a;i$-}`ixC$hN&#MO-~(86Gl!E z+f}<@xt6@lWzjnQ%PE$^`z(bi+llQqRnuo`bJX^je#>J~%)FMD_hMHQb8mG4VIEtp zxS1D`GSybiKq<(4Ok@59YWuL730b~T7zNg}0Jdg^OU;&QfkjG@Qj4Xrz=HB;03ZVd z?W1+FSy$Bm>l>)3>{N&un|=ty_5-Q4N%q1M@-Ka)RxF>72ajF-iFB&XKeP9SwLk445??Lv|BSe#F!LK}0=b7$Fk zSpuWy3V;D4g1lKxb7N1}YCkK%pvVRKpkn4|u5J3gS(g<}h}FI!k{>T)d3ic@EZa zEn|&z>pF#^__WOc*0``eNWl;V{fe89%SqnpCt*YclRAuwj$}sTC1m-8yy}T1#Qku@ z&y9+4W9WLhP+1cXk_DI+Uz2%YqC3?abpZ~k{2nq5`~W8HDRD3LC_F4}#w3&_OpD#= SPUsSs(z|@Q2sG`&)BOunng^Ev delta 222 zcmaFt+G5L>&&$ij00b8uKBp}-oX97`D6>&pn2|3ESElHiMs^G?$Iypx{nlWv1hl0G&E%xHn;^NHwJRqYqIT^^FyivgnXxs~h zM8@*TUW%@a$&)82+KJo+I Date: Wed, 25 Sep 2024 01:58:10 +0300 Subject: [PATCH 8/8] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/notifications.cpython-310.pyc | Bin 0 -> 1627 bytes handlers/notifications.py | 41 ++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 handlers/__pycache__/notifications.cpython-310.pyc create mode 100644 handlers/notifications.py diff --git a/handlers/__pycache__/notifications.cpython-310.pyc b/handlers/__pycache__/notifications.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80b1e136c6ac10cc73f858f63ed8badd6302eb40 GIT binary patch literal 1627 zcmbVM&2Jk;6rb5|f5df5qypuz67j((ja9@cN+XiSRgjV(#X(gqA#3AV+Y7s1vpa5L zYmL$m(O${{iBlDbkK9aYsLPg#(PW^Jd=6?|uB{`S}wQ zNd)7^E5FvB>j?cUHphnyn|I+07hxiZU>B8zlw9oLGWMjhgvDNVWlt$9@mg_JPb+J% zR&Avw5sm0~l(J563FVC!U#ojZgd`n~B$tVfFFTCt~Z2!!z z{I8Z9*AUoF_0h-{M%RFg(ka~*QV>md1{n2H7rlo9u_-4(K1TX8;DM$@bZI>~MxF%NwgJd710ghDCuC zu26fM@?^^jLc8Mm#O{(4$p}LKfyMtp+zGSot4G;E*pEek<0RSB|C&?#vLNuQ;XQW1 zz6AycqwEh%b_l5pA_nXs==V)<^$>`71k8L7%|> z_#A!}YjWmqT^jb%?^AjPzk!V%>CYes@VpUZLBwQ_NY*XKuLqVFD4^#W&{A{iqZbi? fIE`b|l(=QgLG`v>dtUVBP|#zbEKcLJbWZvUDUb8c literal 0 HcmV?d00001 diff --git a/handlers/notifications.py b/handlers/notifications.py new file mode 100644 index 00000000..42c57a76 --- /dev/null +++ b/handlers/notifications.py @@ -0,0 +1,41 @@ +import asyncpg +from datetime import datetime, timedelta +from aiogram import Bot + +from config import DATABASE_URL + +async def notify_expiring_keys(bot: Bot): + try: + conn = await asyncpg.connect(DATABASE_URL) + try: + # Получаем все ключи, которые истекают в течение следующих 10 часов + threshold_time = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 # В миллисекундах + records = await conn.fetch(''' + SELECT tg_id, email, expiry_time FROM keys + WHERE expiry_time <= $1 AND expiry_time > $2 + ''', threshold_time, datetime.utcnow().timestamp() * 1000) + + for record in records: + tg_id = record['tg_id'] + email = record['email'] + expiry_time = record['expiry_time'] + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S') + message = f"Ваш ключ {email} истечет {expiry_date}. Пожалуйста, продлите его." + await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML') + + # Получаем все истекшие ключи + expired_records = await conn.fetch(''' + SELECT tg_id, email FROM keys + WHERE expiry_time <= $1 + ''', datetime.utcnow().timestamp() * 1000) + + for record in expired_records: + tg_id = record['tg_id'] + email = record['email'] + message = f"Ваш ключ {email} уже истек. Пожалуйста, продлите его." + await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML') + + finally: + await conn.close() + except Exception as e: + print(f"Ошибка при отправке уведомлений: {e}")