From 174251965952d2229e3bac389530f44c886ad3ae Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:11:05 +0300 Subject: [PATCH 1/6] Add update user --- handlers/admin/admin_user_editor.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index 18fa6fc1..0f484846 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -115,6 +115,7 @@ async def handle_username_input( ) ) builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}")) + builder.row(InlineKeyboardButton(text="🔄 Обновить клиента", callback_data=f"user_info|{tg_id}")) builder.row( InlineKeyboardButton( text="✉️ Отправить сообщение", @@ -202,6 +203,7 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext, session: ) ) builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}")) + builder.row(InlineKeyboardButton(text="🔄 Обновить клиента", callback_data=f"user_info|{tg_id}")) builder.row( InlineKeyboardButton( text="🔄 Восстановить пробник", @@ -647,6 +649,13 @@ async def handle_user_info( builder.row(InlineKeyboardButton(text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}")) builder.row(InlineKeyboardButton(text="🔄 Восстановить пробник", callback_data=f"restore_trial_{tg_id}")) builder.row(InlineKeyboardButton(text="❌ Удалить клиента", callback_data=f"confirm_delete_user_{tg_id}")) + builder.row(InlineKeyboardButton(text="🔄 Обновить клиента", callback_data=f"user_info|{tg_id}")) + builder.row( + InlineKeyboardButton( + text="✉️ Отправить сообщение", + callback_data=f"send_message_{tg_id}" + ) + ) builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor")) user_info = ( From 93ac01592f1d35ae8d30dce21900c49ce4bf9963 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:19:45 +0300 Subject: [PATCH 2/6] Update send_to_alls --- handlers/admin/admin_panel.py | 53 ++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index 81161293..9e26b137 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -58,7 +58,7 @@ async def handle_admin_message(message: types.Message, state: FSMContext): ) ) builder.row( - InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to_alls") + InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to") ) builder.row( InlineKeyboardButton( @@ -261,14 +261,40 @@ async def export_payments_csv(callback_query: CallbackQuery, session: Any): ) -@router.callback_query(F.data == "send_to_alls", IsAdminFilter()) +@router.callback_query(F.data == "send_to", IsAdminFilter()) async def handle_send_to_all(callback_query: CallbackQuery, state: FSMContext): builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="📢 Отправить всем", callback_data="send_to_all")) + builder.row(InlineKeyboardButton(text="📢 Отправить с подпиской", callback_data="send_to_subscribed")) + builder.row(InlineKeyboardButton(text="📢 Отправить без подписки", callback_data="send_to_unsubscribed")) builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="admin")) await callback_query.message.answer( - "✍️ Введите текст сообщения, который вы хотите отправить всем клиентам 📢🌐:", + "✍️ Выберите группу пользователей и введите текст сообщения для рассылки:", reply_markup=builder.as_markup(), ) + +@router.callback_query(F.data == "send_to_all", IsAdminFilter()) +async def handle_send_to_all(callback_query: CallbackQuery, state: FSMContext): + await state.update_data(send_to="all") + await callback_query.message.answer( + "✍️ Введите текст сообщения для рассылки всем пользователям:" + ) + await state.set_state(UserEditorState.waiting_for_message) + +@router.callback_query(F.data == "send_to_subscribed", IsAdminFilter()) +async def handle_send_to_subscribed(callback_query: CallbackQuery, state: FSMContext): + await state.update_data(send_to="subscribed") + await callback_query.message.answer( + "✍️ Введите текст сообщения для рассылки пользователям с активной подпиской:" + ) + await state.set_state(UserEditorState.waiting_for_message) + +@router.callback_query(F.data == "send_to_unsubscribed", IsAdminFilter()) +async def handle_send_to_unsubscribed(callback_query: CallbackQuery, state: FSMContext): + await state.update_data(send_to="unsubscribed") + await callback_query.message.answer( + "✍️ Введите текст сообщения для рассылки пользователям без активной подписки:" + ) await state.set_state(UserEditorState.waiting_for_message) @@ -279,7 +305,26 @@ async def process_message_to_all( text_message = message.text try: - tg_ids = await session.fetch("SELECT tg_id FROM connections") + state_data = await state.get_data() + send_to = state_data.get('send_to', 'all') + + if send_to == 'all': + tg_ids = await session.fetch("SELECT DISTINCT c.tg_id FROM connections c") + elif send_to == 'subscribed': + tg_ids = await session.fetch(""" + SELECT DISTINCT c.tg_id + FROM connections c + JOIN keys k ON c.tg_id = k.tg_id + WHERE k.expiry_time > CURRENT_TIMESTAMP + """) + elif send_to == 'unsubscribed': + tg_ids = await session.fetch(""" + SELECT c.tg_id + FROM connections c + LEFT JOIN keys k ON c.tg_id = k.tg_id + GROUP BY c.tg_id + HAVING COUNT(k.tg_id) = 0 OR MAX(k.expiry_time) <= CURRENT_TIMESTAMP + """) total_users = len(tg_ids) success_count = 0 From 080a99cd55206cbb356e8fa591a6055495db99c6 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:24:01 +0300 Subject: [PATCH 3/6] Update update_subscription user --- handlers/keys/keys.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 8db9f50b..eb039ff2 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -190,6 +190,13 @@ async def process_callback_view_key(callback_query: types.CallbackQuery, session builder = InlineKeyboardBuilder() + builder.row( + InlineKeyboardButton( + text="🔄 Обновить подписку", + callback_data=f"update_subscription|{key_name}", + ) + ) + builder.row( InlineKeyboardButton(text="🍏 Скачать для iOS", url=DOWNLOAD_IOS), InlineKeyboardButton( @@ -222,15 +229,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery, session InlineKeyboardButton( text="❌ Удалить", callback_data=f"delete_key|{key_name}" ), - ) - - if not key.startswith(PUBLIC_LINK): - builder.row( - InlineKeyboardButton( - text="🔄 Обновить подписку", - callback_data=f"update_subscription|{key_name}", - ) - ) + ) builder.row( InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile") From 932ef51de42dd2ee540bba0a8b1548f2041c3833 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:32:18 +0300 Subject: [PATCH 4/6] Add update_all_keys --- handlers/notifications.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/handlers/notifications.py b/handlers/notifications.py index 7a2b9271..4678a80a 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -413,3 +413,29 @@ async def check_online_users(): logger.error( f"Не удалось проверить пользователей на сервере {server_id}: {e}" ) + + +async def update_all_keys(): + try: + conn = await asyncpg.connect(DATABASE_URL) + keys = await conn.fetch("SELECT tg_id, client_id, email, expiry_time, server_id FROM keys") + + for key in keys: + tg_id = key['tg_id'] + client_id = key['client_id'] + email = key['email'] + expiry_time = key['expiry_time'] + cluster_id = key['server_id'] + + try: + await update_key_on_cluster(tg_id, client_id, email, expiry_time, cluster_id) + await store_key(tg_id, client_id, email, expiry_time, key['key'], cluster_id, conn) + logger.info(f"Ключ {client_id} успешно обновлен и сохранен") + except Exception as e: + logger.error(f"Ошибка при обновлении и сохранении ключа {client_id}: {e}") + + logger.info("Все ключи успешно обновлены и сохранены") + except Exception as e: + logger.error(f"Ошибка при обновлении всех ключей: {e}") + finally: + await conn.close() From 681fd7d92c3df16a67460257981cc511656fc3ef Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:43:04 +0300 Subject: [PATCH 5/6] Add get_usd_rate --- handlers/utils.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/handlers/utils.py b/handlers/utils.py index eeed52d8..a2ea3721 100644 --- a/handlers/utils.py +++ b/handlers/utils.py @@ -1,6 +1,8 @@ import random import re +import json +import aiohttp import asyncpg from bot import bot @@ -9,6 +11,21 @@ from database import get_servers_from_db from logger import logger +async def get_usd_rate(): + try: + async with aiohttp.ClientSession() as session: + async with session.get('https://www.cbr-xml-daily.ru/daily_json.js') as response: + if response.status == 200: + data = await response.text() + usd = float(json.loads(data)['Valute']['USD']['Value']) + else: + usd = 100 # Default value if request fails + except Exception as e: + logger.exception(f"Error fetching USD rate: {e}") + usd = 100 # Default value if an exception occurs + return usd + + def sanitize_key_name(key_name: str) -> str: """ Очищает название ключа, оставляя только допустимые символы. From 5289548fd9b4e825d75469804d7c5916b95b804e Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Sun, 5 Jan 2025 23:46:45 +0300 Subject: [PATCH 6/6] Fix float get_usd_rate --- handlers/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/handlers/utils.py b/handlers/utils.py index a2ea3721..a2d26b0b 100644 --- a/handlers/utils.py +++ b/handlers/utils.py @@ -19,10 +19,10 @@ async def get_usd_rate(): data = await response.text() usd = float(json.loads(data)['Valute']['USD']['Value']) else: - usd = 100 # Default value if request fails + usd = float(100) # Default value if request fails except Exception as e: logger.exception(f"Error fetching USD rate: {e}") - usd = 100 # Default value if an exception occurs + usd = float(100) # Default value if an exception occurs return usd