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 9876cae9..1c9d0b6e 100644 Binary files a/handlers/__pycache__/keys.cpython-310.pyc and b/handlers/__pycache__/keys.cpython-310.pyc differ diff --git a/handlers/__pycache__/pay.cpython-310.pyc b/handlers/__pycache__/pay.cpython-310.pyc index f9a2f72c..544e555d 100644 Binary files a/handlers/__pycache__/pay.cpython-310.pyc and b/handlers/__pycache__/pay.cpython-310.pyc differ diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index 44a606fa..b038020a 100644 Binary files a/handlers/__pycache__/profile.cpython-310.pyc and b/handlers/__pycache__/profile.cpython-310.pyc differ diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index 963cd457..395da8ca 100644 Binary files a/handlers/__pycache__/start.cpython-310.pyc and b/handlers/__pycache__/start.cpython-310.pyc differ 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