From 8538050fb5a3d3923e417af80da119a52176ac95 Mon Sep 17 00:00:00 2001 From: Vladless Date: Mon, 21 Oct 2024 02:47:59 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BD=D0=B0=D0=BF=D0=BE=D0=BC=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=20=D1=81=D1=83=D1=82?= =?UTF-8?q?=D0=BA=D0=B8=20=D1=83=20=D0=B0=D0=BA=D1=82=D0=B8=D0=B2=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database.py | 7 + .../__pycache__/notifications.cpython-310.pyc | Bin 4519 -> 3184 bytes handlers/notifications.py | 143 ++++-------------- 3 files changed, 34 insertions(+), 116 deletions(-) diff --git a/database.py b/database.py index b50d1896..07c87fa9 100644 --- a/database.py +++ b/database.py @@ -52,6 +52,13 @@ async def init_db(): ''') except asyncpg.exceptions.DuplicateColumnError: pass + try: + await conn.execute(''' + ALTER TABLE keys + ADD COLUMN notified_24h BOOLEAN NOT NULL DEFAULT FALSE + ''') + except asyncpg.exceptions.DuplicateColumnError: + pass await conn.close() diff --git a/handlers/__pycache__/notifications.cpython-310.pyc b/handlers/__pycache__/notifications.cpython-310.pyc index f211f260b60cc8c4110a3271f13df5470afd0583..89fe99c3b32ab3908a1d3701f97589448558caca 100644 GIT binary patch delta 1580 zcmbtSPi)&%7=Q2C`R62#FY+NAgo zZ6lks&0!pX0xkzs4H9txaY_pj)5?0`z}3E5LPA_%98gc3*v4lkA+Sp9fUWm^?|nai ze||r|FUF43d?g;|5IpN258et0`}{8iC#*lnSZx4;F<9TjQcn?tws7@#4K=Vvn)nq& z(N$#P>W>=JV4J8BXd;SJ$RwS!7NmqS@8O|7!u*Y}H1IEkHE<(Dg9c-eCey7Ps2PDP z=;j48Xas3!7?~mPVenz_5%3Z49C(gK4Q>z7m=OjWhbCboLh)(T#El5$n|Kf137Sy@ zH8>+=M4P1303&D`r3t7a45NPZ%?ti8Nu$R8W15%|gJEtyeV9(`z9wm8#%`6|B1TnSOVUw81PMtUW3+&7K$;{SzT~Qz4&WWY@4Yj&&RikIEYG}WvzYOu?&vthu`*Zui zZrk73NA`F2A?)tkckOmgvOlt0&O4HQ&pveCcWz7Y#i6q!Ia_wy+4NGtwj}*|s3X~T zoh@e@a_`wK0JbIPwvV^%Z>6bo-l+w7NA|%aH@lKAE$3z={R|{>_AVgXP^VYHOY|mi zc5+fRnYl5MS((UCDRVWKc{P`rv%chBh`W)(a!D?0izQlZcNx9KE>52>TF0Tmn&MF| z2|PmjW!U|^^Y#DbIQ2A+)8~5}Ta&deMM;0oXZWcvO$($wg5A54eE`C>L2I99PSUep z^}a;^t%;uSi+%_?AHurtY=KV9 zXm&t}dpKLDl*?|cpeu^^AH9CAs1+4iEmoGPe_otAS3D7W35It=id?8DRCR-$jq)s% zR6MbjG0%*99YVbkqusCl>oJprOX1?;U6c!Lbfa1X;@!)v mq!4hP#0*l-TAv6%;LBD%IWde=I0h34U_GCP6(j-?nZE#yHV>5m literal 4519 zcmbVQO>Er86(+glF16Z!t!+uRV*)Jz%4AxXc9DPf?QLz*0GmDcJ2H)wsYvQ7cS6C=GXv5ja~y3DA2g?4Yks)>l8s& z;Be;6n>X)$^UWKM+;loA!0+bQsmfOag77sqI)5@WUV+C8p&$YgZNU+A!4Y*)#I|Hh zx&-f-9dl$|b`)K4;(DC-$#%j~b(Pl@yT?iDNvBuu;(0&#a|6 z*QDfB!}s0VPHoi)PiY)+)xlOO0BG zEyIVn{~*x4pv8-SkBu(4jx~DOHp%FOb$N8wwOs=s8(o0Ku`TM2c8>Q(>PvJt49>&j zA+c27HAs-yUSY>dztHUQRnp`41U)121<}2PX}r8%$gjbBMN|CjyxO9 zwjs5p7lcd0vf$^J{w4~|tSBt!{Q@}{C}4Le$U^y}b%iko`Ncr>clo|Teu&2xaK#5UgdO$}VfHcEqQEhcmf)n~jLHC`UP3OdOWjPu zu{&s#j!r+s!xQqfvfwtTSN%s!5SDKywa0$5lV>MiJ*W8||7JMZ=^dfB2-)n1!Oq75c5NCy)fjwFhsPW)5nIUjt$K= z2BC9I3-5(@S}Uy&TEEf`jU7I$wXU{S!b4Iy%#&|g`3);BR@I%WQ#8bji*5W_LAwd%H2t9lFV z!?S_x2-l(iQ+RH(6c5jL4PZbGSd*>tuLL~6`Da3-5A{C^H(T#&;ob0y)^A$xXvgPH zHco*2_~_h84dz{GT?35Yf#(`D!39A0HgE9?`g13|uW^m5Q2mr!z~c;Mo2?ay;1)M_ z2u;7eWx6z?@c=);h``e4e4@=^E&Q@|6(It<5OjD)1Dk-^NOH?jl!bR&tF8CK&%)2U zAUTFpFULlX%r{be{)i1YIg?MHn>q`hEs64i=>z%bLC|2yx=B3?KMyRHLOhoMEBs1Y ze(C(1@YymM?T6XPb6N+Z&&`~l)S@l1%_|`K!Li2A!D#rq*1O=^O+a)riUv@70B&Po zo8c{x-_bZM4*<;f5Iu#Dww0EU_KWu(rJse?VgBn(QF~-yhv7mSas!9>Nw^<*qP6O8A3b{1_zO%MQTb-;*Wni$t^kMY9!K~y&RfJ17HNlh$Y@xW zO|3J<3~MlOGrWs?1z)XIjjz*6q}sVTk+yt3T$9_d`nZd5MIXR?_dy5(+$^_w$1Pac zbzF4t@bjqa0Z!Ag`67-04~CEj?Y)4ewnlZ%&i6qnMxO&+&U5-a)Ru=P;9LMo@8tO> zau~l>!;iMmXk7;?11VYRa@ATi>eQXD+E(Kb=yWBB@Gk6Jo*fY6&de~vv&r|{6sv$I zfC53?i_!{i@nc$PkR?oSsaCF6VE?+cnpO5G<}NBh!Qu_STyqyG9EL)4o$`FssWWBX z^2-Y>=9pJltP1y-94O9H*TFL|Qc*ph&SR5e!lL*APBDqHl!q4{Q1i@+#S+#Ps|;5% z)-!pfY}N6i!IU}$n{*cr!E8hEf+q8GUCNe}P)wf@>3g z7_$Vm$}S}yi$@iT$)9RiJdMROP()!Fwl(jwBtQG3di&_aD-=t#?^J3#!Lvj=0?}ew zzLhLuY|TfXMZ;M2?l2EGu%48)`ek4okx02j14uak@qIy+& z6pv-PtYoC5RFL9gN=km4Qwm^7l~foBC5#+|xubCfxd8J?Qc6@GrDRFkx7{QC_2A*0 zxQ|<>Q{Zw**Y33~88 zNqev}-R|U9c+M%#E+(4|Sbt6!yaTd$lyNy-IB`JY*5<1fnnPopPn=oEcm8h!bIyd+ z%=wDs1KRNa7DV?-46I}2_|=s!pL#sH1E0jNDi1|DK?N9708kEK#dkkEaVaMiV*dpT CI@Tos diff --git a/handlers/notifications.py b/handlers/notifications.py index 8d829a29..c2257ced 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -1,11 +1,9 @@ from datetime import datetime, timedelta - import asyncpg from aiogram import Bot, Router, types from aiogram.filters import Command from aiogram.fsm.state import State, StatesGroup from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup - from auth import login_with_credentials from bot import bot from client import delete_client, extend_client_key @@ -21,57 +19,37 @@ async def notify_expiring_keys(bot: Bot): try: conn = await asyncpg.connect(DATABASE_URL) try: - # Напоминание за 10 часов - threshold_time_10h = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 - records_10h = await conn.fetch(''' + current_time = datetime.utcnow().timestamp() * 1000 + threshold_time_10h = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 + threshold_time_24h = (datetime.utcnow() + timedelta(days=1)).timestamp() * 1000 + + # Уведомления за 10 часов + records = await conn.fetch(''' SELECT tg_id, email, expiry_time, client_id, server_id FROM keys WHERE expiry_time <= $1 AND expiry_time > $2 AND notified = FALSE - ''', threshold_time_10h, datetime.utcnow().timestamp() * 1000) + ''', threshold_time_10h, current_time) - for record in records_10h: + for record in records: tg_id = record['tg_id'] email = record['email'] expiry_time = record['expiry_time'] server_id = record['server_id'] - time_left = (expiry_time / 1000) - datetime.utcnow().timestamp() - hours_left = max(0, int(time_left // 3600)) + # Логика формирования и отправки сообщения для уведомлений за 10 часов + message = f"🔔 Уведомление: Ваш ключ для сервера {server_id} истекает через 10 часов.\n" \ + f"Email: {email}\n" \ + f"Дата истечения: {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}" + + await bot.send_message(tg_id, message) - expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S') - balance = await get_balance(tg_id) + # Обновление статуса уведомления + await conn.execute('UPDATE keys SET notified = TRUE WHERE client_id = $1', record['client_id']) - keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton(text='1 месяц (100 руб.)', callback_data=f'renew_plan|1|{record["client_id"]}'), - InlineKeyboardButton(text='3 месяца (285 руб.)', callback_data=f'renew_plan|3|{record["client_id"]}') - ], - [ - InlineKeyboardButton(text='6 месяцев (540 руб.)', callback_data=f'renew_plan|6|{record["client_id"]}'), - InlineKeyboardButton(text='1 год (1000 руб.)', callback_data=f'renew_plan|12|{record["client_id"]}') - ], - [ - InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance'), - InlineKeyboardButton(text='Назад', callback_data='back_to_main') - ] - ]) - - message = (f"Ваш ключ {email} истечет и будет удален через {hours_left} часов " - f"({expiry_date}). Пожалуйста, продлите его.\n" - f"Ваш текущий баланс: {balance:.2f} руб.") - - try: - await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML', reply_markup=keyboard) - - await conn.execute('UPDATE keys SET notified = TRUE WHERE client_id = $1', record['client_id']) - except Exception as e: - print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") - - # Напоминание за 24 часа - threshold_time_24h = (datetime.utcnow() + timedelta(days=1)).timestamp() * 1000 + # Уведомления за 24 часа records_24h = await conn.fetch(''' SELECT tg_id, email, expiry_time, client_id, server_id FROM keys WHERE expiry_time <= $1 AND expiry_time > $2 AND notified_24h = FALSE - ''', threshold_time_24h, datetime.utcnow().timestamp() * 1000) + ''', threshold_time_24h, current_time) for record in records_24h: tg_id = record['tg_id'] @@ -83,86 +61,19 @@ async def notify_expiring_keys(bot: Bot): hours_left = max(0, int(time_left // 3600)) expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S') - balance = await get_balance(tg_id) - - keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [ - InlineKeyboardButton(text='1 месяц (100 руб.)', callback_data=f'renew_plan|1|{record["client_id"]}'), - InlineKeyboardButton(text='3 месяца (285 руб.)', callback_data=f'renew_plan|3|{record["client_id"]}') - ], - [ - InlineKeyboardButton(text='6 месяцев (540 руб.)', callback_data=f'renew_plan|6|{record["client_id"]}'), - InlineKeyboardButton(text='1 год (1000 руб.)', callback_data=f'renew_plan|12|{record["client_id"]}') - ], - [ - InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance'), - InlineKeyboardButton(text='Назад', callback_data='back_to_main') - ] - ]) - - message = (f"Ваш ключ {email} истечет через {hours_left} часов " - f"({expiry_date}). Пожалуйста, продлите его.\n" - f"Ваш текущий баланс: {balance:.2f} руб.") - - try: - await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML', reply_markup=keyboard) - - await conn.execute('UPDATE keys SET notified_24h = TRUE WHERE client_id = $1', record['client_id']) - except Exception as e: - print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") - - # Обработка истекших ключей - expired_records = await conn.fetch(''' - SELECT tg_id, email, client_id, server_id FROM keys - WHERE expiry_time <= $1 - ''', datetime.utcnow().timestamp() * 1000) - - for record in expired_records: - tg_id = record['tg_id'] - email = record['email'] - client_id = record['client_id'] - server_id = record['server_id'] - balance = await get_balance(tg_id) - if balance >= 100: - # Продлеваем ключ на 1 месяц и списываем 100 рублей - new_expiry_time = datetime.utcnow() + timedelta(days=30) - await conn.execute(''' - UPDATE keys SET expiry_time = $1 WHERE client_id = $2 - ''', new_expiry_time.timestamp() * 1000, client_id) + # Логика формирования и отправки сообщения для уведомлений за 24 часа + message_24h = f"⏳ Уведомление: Ваш ключ для сервера {server_id} истекает через 24 часа.\n" \ + f"Email: {email}\n" \ + f"Осталось времени: {hours_left} часов\n" \ + f"Дата истечения: {expiry_date}\n" \ + f"Баланс: {balance:.2f} руб." - # Списываем 100 рублей с баланса - await update_balance(tg_id, -100) + await bot.send_message(tg_id, message_24h) - session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) - await extend_client_key(session, server_id, client_id, 30) - - message = (f"Ваш ключ {email} был автоматически продлен на 1 месяц, так как у вас на балансе было достаточно средств. " - f"Новый срок истечения: {new_expiry_time.strftime('%Y-%m-%d %H:%M:%S')}.\n" - f"Ваш новый баланс: {balance - 100:.2f} руб.") - - try: - await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML') - except Exception as e: - print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") - else: - # Удаляем ключ, если баланс недостаточный - await conn.execute('DELETE FROM keys WHERE client_id = $1', client_id) - - session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) - await delete_client(session, server_id, client_id) - - keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='В профиль', callback_data='view_profile')] - ]) - - message = f"Ваш ключ {email} истек, и был удален." - - try: - await bot.send_message(chat_id=tg_id, text=message, parse_mode='HTML', reply_markup=keyboard) - except Exception as e: - print(f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя.") + # Обновление статуса уведомления за 24 часа + await conn.execute('UPDATE keys SET notified_24h = TRUE WHERE client_id = $1', record['client_id']) finally: await conn.close()