From 864c099b2fbc271b2e6c0e6dc04d486d22c579f2 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 18 Sep 2024 01:44:27 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 1 + handlers/__pycache__/keys.cpython-310.pyc | Bin 1410 -> 1624 bytes handlers/__pycache__/pay.cpython-310.pyc | Bin 132 -> 4504 bytes handlers/__pycache__/profile.cpython-310.pyc | Bin 6487 -> 2645 bytes handlers/__pycache__/start.cpython-310.pyc | Bin 1574 -> 1308 bytes handlers/keys.py | 8 +- handlers/pay.py | 116 ++++++++++++++ handlers/profile.py | 160 ++++--------------- handlers/start.py | 45 +++--- key_management.py | 15 ++ 10 files changed, 193 insertions(+), 152 deletions(-) diff --git a/bot.py b/bot.py index 1d5f2ec8..cdc9dc8b 100644 --- a/bot.py +++ b/bot.py @@ -20,6 +20,7 @@ dp.include_router(stats.router) dp.include_router(expiry.router) dp.include_router(balance.router) dp.include_router(key_management_router) +dp.include_router(pay.router) diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 9876cae941c05a1b03de4de2c69ad1d236630b03..1c9d0b6eb2f9270d303421b605076ebcad9fae56 100644 GIT binary patch delta 830 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 diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index 44a606fab2600ad127eab1506d5ecb8307dfb4b8..b038020a4514b9f7d9f83edef609f577dd1c4931 100644 GIT binary patch delta 1503 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 diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index 963cd457fadf591cc94f5f8c43eb14cfa7150551..395da8ca0615ae631442df57fe507ff9594d9ae1 100644 GIT binary patch delta 527 zcmYjNy=xRf6rcCzWA`?@M?ed6+UpXm1VN%f#6k;g5)jfHd-vv++`}iK8MVn=kM9fK~E=CkE_QxBmBAxNv z{c87Q^VvP>kkoLnMoPycK>>xIiIZ61oI5N@EwQjr^BiiCLyib$aM88I6!yrO!(Z}@al(yY+i;H=8;yUt>`0}6Zi54!7hIsgCw delta 705 zcmYjP&1(}u6rY)${b;gl(R%4iqA#!Pi^)m$M=WBN8QcPwVyLivfEZEqu1 zUC-uTtk)gawaZQkkJU5)t6<+{*=OK7nBg||5F<=sv`5+munKi0RsieFNH7IF!BPoU zJsglS4iqp7rj~J;vUCr(wcskMk^l!9GkfF)V(2=;$WuJZEOw}eL5SHT%Rq5GKvk7Z z1^T#>A!sA}={;V?)3H^uYqg?XS}ic!v;90=SG)`t$l|d$b+6*A7obtk^-oIvx#&k* zq8~mab5@*Avd;ar3U7s)axQ$JWWvkJnGJtgYTtr#BtArsbK;{IM4MtS>PB19lc*Ct z1u7@@px7hP7hlBaR1%5L;;R_Ku|ZDkLV6^=iQT9xb3@1t&gL~PpOwRJM6t@9wOWho zl8_G`Yj(q3cX%Ef;ZRvzm@4|7*K8F2_Yh~tspR6}59R3b!}4^tZdV$Gy3_Crt4^!f zv^jf+_-ts>E2rl{KsG5ciHYSV3dIN8_;bV{3NTpvJEQUwAPhV7_BAeNF4k`z3`fHA gS)j(;oKzU+>rLj@oQraNToYUdnRo_k@U3b54-nSQ2LJ#7 diff --git a/handlers/keys.py b/handlers/keys.py index 043b4fc7..331f0764 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -2,6 +2,8 @@ from aiogram import types, Router import aiosqlite from config import DATABASE_PATH from bot import bot +from datetime import datetime + router = Router() @router.callback_query(lambda c: c.data == 'view_keys') @@ -11,7 +13,7 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): try: async with aiosqlite.connect(DATABASE_PATH) as db: async with db.execute(''' - SELECT k.key + SELECT k.key, c.expiry_time FROM keys k JOIN connections c ON k.client_id = c.client_id WHERE c.tg_id = ? @@ -20,7 +22,9 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): if record: key = record[0] - response_message = f"Ваш ключ:\n
{key}
" + expiry_time = record[1] + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime("%Y-%m-%d %H:%M:%S") + response_message = f"Ваш ключ:\n
{key}
\nДата окончания: {expiry_date}" else: response_message = "У вас нет ключей." diff --git a/handlers/pay.py b/handlers/pay.py index e69de29b..9c06aeea 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -0,0 +1,116 @@ +from aiogram import types, Router +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import State, StatesGroup +import aiosqlite +from config import DATABASE_PATH, ADMIN_ID +from database import get_balance, update_balance +from bot import bot + +router = Router() + +class ReplenishBalanceState(StatesGroup): + choosing_transfer_method = State() + waiting_for_admin_confirmation = State() + +@router.callback_query(lambda c: c.data == 'replenish_balance') +async def process_callback_replenish_balance(callback_query: types.CallbackQuery, state: FSMContext): + await state.set_state(ReplenishBalanceState.choosing_transfer_method) + keyboard = InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')], + [InlineKeyboardButton(text='По СБП', callback_data='transfer_method_sbp')] + ]) + await callback_query.message.answer("Выберите метод перевода:", reply_markup=keyboard) + await callback_query.answer() + +@router.callback_query(lambda c: c.data.startswith('transfer_method_')) +async def process_transfer_method_selection(callback_query: types.CallbackQuery, state: FSMContext): + data = callback_query.data.split('_', 2) + + if len(data) != 3: + await callback_query.answer("Неверные данные для выбора метода перевода.") + return + + transfer_method = data[2] # 'requisites' или 'sbp' + + if transfer_method == 'requisites': + message = ( + "Реквизиты для пополнения:\n" + f'\n' + "Банк: Пример Банк\n" + "Расчетный счет: 12345678901234567890\n" + "Получатель: ИП Иванов Иван Иванович" + f'\n' + "после перевода отправьте чек и ждите подтверждения" + ) + elif transfer_method == 'sbp': + message = ( + "СБП для пополнения:\n" + f'\n' + "Номер банка: 1234567890\n" + "ФИО: Иванов Иван Иванович\n" + f'\n' + "после перевода отправьте чек и ждите подтверждения" + ) + else: + await callback_query.answer("Неверный метод перевода.") + return + + await callback_query.message.answer(message) + await callback_query.answer() + +@router.callback_query(lambda c: c.data.startswith('admin_')) +async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext): + if callback_query.from_user.id != ADMIN_ID: + await callback_query.answer("Вы не являетесь администратором.") + return + + data = callback_query.data.split('_', 4) + + if len(data) < 4: + await callback_query.answer("Неверные данные для обработки запроса.") + return + + action = data[1] # 'confirm' или 'decline' + user_id_str = data[3] + + try: + user_id = int(user_id_str) # Преобразование user_id в целое число + + if action == 'confirm': + + amount_str = data[4].split('_')[0] + + # Убедимся, что amount_str действительно является числом + if amount_str.isdigit(): + amount = int(amount_str) # Преобразование amount в целое число + else: + await callback_query.answer("Неверная сумма для пополнения. Проверьте формат данных.") + print(f"Ошибка преобразования суммы: amount_str='{amount_str}'") # Отладочное сообщение в консоль + return + + async with aiosqlite.connect(DATABASE_PATH) as db: + # Обновляем баланс пользователя + await update_balance(user_id, amount) + await db.commit() + + balance = await get_balance(user_id) + await callback_query.message.answer(f"Баланс пользователя успешно пополнен на {amount} единиц.\nТекущий баланс: {balance}") + + # Сообщаем пользователю о пополнении + await bot.send_message(user_id, f"Ваш баланс был успешно пополнен на {amount} RUB.") + + elif action == 'decline': + await callback_query.message.answer("Пополнение баланса отклонено.") + + # Сообщаем пользователю об отклонении + await bot.send_message(user_id, "Ваш запрос на пополнение баланса был отклонен.") + + except Exception as e: + # Отладочное сообщение + await callback_query.message.answer(f"Ошибка при пополнении баланса: {e}") + print(f"Ошибка при пополнении баланса: {e}") # Отладочное сообщение в консоль + + # Очистка состояния + await state.clear() + await callback_query.answer() diff --git a/handlers/profile.py b/handlers/profile.py index d51e4d96..03eefefd 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -1,12 +1,11 @@ -from aiogram import Bot, Dispatcher, Router, types -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery +from aiogram import types, Router +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from datetime import datetime import aiosqlite -from config import DATABASE_PATH, ADMIN_ID -from database import get_balance, update_balance, has_active_key -from bot import bot +from config import DATABASE_PATH +from database import get_balance, has_active_key router = Router() @@ -14,14 +13,15 @@ class ReplenishBalanceState(StatesGroup): choosing_transfer_method = State() waiting_for_admin_confirmation = State() -@router.callback_query(lambda c: c.data == 'view_profile') async def process_callback_view_profile(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id username = callback_query.from_user.username try: - # Получаем активный ключ + # Проверяем наличие активного ключа email = await has_active_key(tg_id) + expiry_date = "Нет активного ключа" + if email: async with aiosqlite.connect(DATABASE_PATH) as db: async with db.execute( @@ -29,136 +29,32 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta (tg_id, int(datetime.utcnow().timestamp() * 1000)) ) as cursor: record = await cursor.fetchone() - expiry_date = "Неизвестно" if record is None else datetime.utcfromtimestamp(record[0] / 1000).strftime("%Y-%m-%d %H:%M:%S") - - balance = await get_balance(tg_id) - profile_message = ( - f"Профиль клиента:\n" - f"Никнейм: @{username}\n" - f"Email: {email}\n" - f"Дата окончания ключа: {expiry_date}\n" - f"Баланс: {balance}\n" - ) - - button_view_keys = InlineKeyboardButton(text='Мои ключи', callback_data='view_keys') - button_replenish_balance = InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') - keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_view_keys], [button_replenish_balance]]) - - profile_message += "Вы можете просмотреть ваши ключи или пополнить баланс ниже:" - else: - profile_message = "У вас нет активных ключей." - keyboard = None + if record: + expiry_date = datetime.utcfromtimestamp(record[0] / 1000).strftime("%Y-%m-%d %H:%M:%S") + + balance = await get_balance(tg_id) + profile_message = ( + f"Профиль \n\n" + f"ID: @{tg_id}\n" + f"Ключ: {email if email else 'Нет активного ключа'}\n" + f"Баланс: {balance} RUB\n" + ) + + # Кнопки для действий в профиле + button_view_keys = InlineKeyboardButton(text='Мои ключи', callback_data='view_keys') + button_replenish_balance = InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') + keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_view_keys], [button_replenish_balance]]) + + profile_message += "\nВы можете просмотреть ваши ключи или пополнить баланс ниже:" except Exception as e: profile_message = f"Ошибка при получении данных профиля: {e}" keyboard = None - # Отправляем сообщение с клавиатурой внизу + # Отправляем сообщение с клавиатурой await callback_query.message.reply(profile_message, reply_markup=keyboard) await callback_query.answer() - -@router.callback_query(lambda c: c.data == 'replenish_balance') -async def process_callback_replenish_balance(callback_query: types.CallbackQuery, state: FSMContext): - await state.set_state(ReplenishBalanceState.choosing_transfer_method) - keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')], - [InlineKeyboardButton(text='По СБП', callback_data='transfer_method_sbp')] - ]) - await callback_query.message.answer("Выберите метод перевода:", reply_markup=keyboard) - await callback_query.answer() - -@router.callback_query(lambda c: c.data.startswith('transfer_method_')) -async def process_transfer_method_selection(callback_query: types.CallbackQuery, state: FSMContext): - data = callback_query.data.split('_', 2) - - if len(data) != 3: - await callback_query.answer("Неверные данные для выбора метода перевода.") - return - - transfer_method = data[2] # 'requisites' или 'sbp' - amount = 100 # Можно заменить на 300, если нужно - user_id = callback_query.from_user.id - - # Сохраняем данные в состояние - await state.update_data(transfer_method=transfer_method, amount=amount, user_id=user_id) - - # Выводим состояние для отладки - state_data = await state.get_data() - print("Сохраненные данные состояния:", state_data) - - # Отправляем запрос на подтверждение администратору - admin_message = ( - f"Запрос на пополнение баланса:\n" - f"Метод перевода: {transfer_method.replace('requisites', 'По реквизитам').replace('sbp', 'По СБП')}\n" - f"Сумма: {amount} единиц\n" - f"Пользователь: {user_id}\n\n" - f"Подтвердите или отклоните пополнение." - ) - keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='Подтвердить', callback_data=f'admin_confirm_replenish_{user_id}_{amount}_{transfer_method}')], - [InlineKeyboardButton(text='Отклонить', callback_data=f'admin_decline_replenish_{user_id}')] - ]) - - # Отправляем сообщение администратору - await callback_query.message.answer("Запрос на пополнение отправлен администратору.") - await bot.send_message(ADMIN_ID, admin_message, reply_markup=keyboard) - - await state.set_state(ReplenishBalanceState.waiting_for_admin_confirmation) - await callback_query.answer() - -@router.callback_query(lambda c: c.data.startswith('admin_')) -async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext): - if callback_query.from_user.id != ADMIN_ID: - await callback_query.answer("Вы не являетесь администратором.") - return - - data = callback_query.data.split('_', 4) - - if len(data) < 4: - await callback_query.answer("Неверные данные для обработки запроса.") - return - - action = data[1] # 'confirm' или 'decline' - user_id_str = data[3] - - try: - user_id = int(user_id_str) # Преобразование user_id в целое число - - if action == 'confirm': - - amount_str = data[4].split('_')[0] - - # Убедимся, что amount_str действительно является числом - if amount_str.isdigit(): - amount = int(amount_str) # Преобразование amount в целое число - else: - await callback_query.answer("Неверная сумма для пополнения. Проверьте формат данных.") - print(f"Ошибка преобразования суммы: amount_str='{amount_str}'") # Отладочное сообщение в консоль - return - - async with aiosqlite.connect(DATABASE_PATH) as db: - # Обновляем баланс пользователя - await update_balance(user_id, amount) - await db.commit() - - balance = await get_balance(user_id) - await callback_query.message.answer(f"Баланс пользователя успешно пополнен на {amount} единиц.\nТекущий баланс: {balance}") - - # Сообщаем пользователю о пополнении - await bot.send_message(user_id, f"Ваш баланс был успешно пополнен на {amount} RUB.") - - elif action == 'decline': - await callback_query.message.answer("Пополнение баланса отклонено.") - - # Сообщаем пользователю об отклонении - await bot.send_message(user_id, "Ваш запрос на пополнение баланса был отклонен.") - - except Exception as e: - # Отладочное сообщение - await callback_query.message.answer(f"Ошибка при пополнении баланса: {e}") - print(f"Ошибка при пополнении баланса: {e}") # Отладочное сообщение в консоль - - # Очистка состояния - await state.clear() - await callback_query.answer() +@router.callback_query(lambda c: c.data == 'view_profile') +async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext): + await process_callback_view_profile(callback_query, state) diff --git a/handlers/start.py b/handlers/start.py index f2b3c05a..ddac6a2e 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -4,30 +4,39 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeybo router = Router() -@router.message(Command("start")) -async def start_command(message: types.Message): - welcome_text = "Добро пожаловать! Вы можете создать ключ для подключения VPN, просмотреть статистику использования, узнать дату окончания ключа или просмотреть ваш профиль." +async def start_command(message: types.Message, callback_data: str = None): + welcome_text = ( + "Добро пожаловать! Вы можете создать ключ для подключения VPN, " + "просмотреть статистику использования, узнать дату окончания ключа или " + "просмотреть ваш профиль." + ) + # Создаем кнопки для команд button_create_key = InlineKeyboardButton(text='Создать ключ', callback_data='create_key') - button_view_stats = InlineKeyboardButton(text='Посмотреть статистику', callback_data='view_stats') - button_view_expiry = InlineKeyboardButton(text='Дата окончания ключа', callback_data='view_expiry') +# button_view_stats = InlineKeyboardButton(text='Посмотреть статистику', callback_data='view_stats') +# button_view_expiry = InlineKeyboardButton(text='Дата окончания ключа', callback_data='view_expiry') button_view_profile = InlineKeyboardButton(text='Мой профиль', callback_data='view_profile') - - # Основное меню - keyboard = InlineKeyboardMarkup(inline_keyboard=[ + + # Создаем клавиатуру с кнопками + inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [button_create_key], - [button_view_stats], - [button_view_expiry], +# [button_view_stats], +# [button_view_expiry], [button_view_profile] ]) - - # Отправка приветственного сообщения - await message.reply(welcome_text, reply_markup=keyboard) - # Кнопка "В начало" для возвращения - main_menu_keyboard = ReplyKeyboardMarkup( - keyboard=[[KeyboardButton(text="В начало")]], + # Создаем кнопку для возврата в начало + reply_keyboard = ReplyKeyboardMarkup( + keyboard=[ + [KeyboardButton(text="В начало")], + [KeyboardButton(text="Мой профиль")] + ], resize_keyboard=True ) - - await message.answer("Чтобы вернуться в начало, нажмите кнопку ниже:", reply_markup=main_menu_keyboard) + + # Отправляем сообщение с кнопками + await message.reply(welcome_text, reply_markup=inline_keyboard) + await message.answer( + "", + reply_markup=reply_keyboard + ) \ No newline at end of file diff --git a/key_management.py b/key_management.py index 3709931b..5ba71195 100644 --- a/key_management.py +++ b/key_management.py @@ -15,9 +15,11 @@ import re from bot import dp from handlers.start import start_command from bot import bot +from handlers.profile import process_callback_view_profile router = Router() + def escape_markdown(text: str) -> str: """ Экранирование специальных символов для Markdown. @@ -39,10 +41,23 @@ async def process_callback_create_key(callback_query: types.CallbackQuery, state await state.set_state(Form.waiting_for_key_name) await callback_query.answer() + @dp.message() async def handle_text(message: types.Message, state: FSMContext): current_state = await state.get_state() + if message.text == "Мой профиль": + # Создайте фейковый объект CallbackQuery для вызова функции + callback_query = types.CallbackQuery( + id="1", # Используйте уникальный идентификатор + from_user=message.from_user, + chat_instance='', + data='view_profile', + message=message + ) + await process_callback_view_profile(callback_query, state) + return + if message.text == "/start": await start_command(message) return