From b181aff960e89e6ae116a8ca16fabd909caf33d1 Mon Sep 17 00:00:00 2001 From: Vlad Date: Tue, 17 Sep 2024 23:51:03 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E?= =?UTF-8?q?=D1=89=D0=B0=D1=8F=20=D0=BE=D0=BF=D0=BB=D0=B0=D1=82=D0=B0=20?= =?UTF-8?q?=D1=81=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2=D0=B5=D1=80=D0=B6=D0=B4?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 4 +- database.py | 9 ++ handlers/__pycache__/pay.cpython-310.pyc | Bin 0 -> 132 bytes handlers/__pycache__/profile.cpython-310.pyc | Bin 2173 -> 6487 bytes handlers/__pycache__/start.cpython-310.pyc | Bin 1612 -> 1574 bytes handlers/pay.py | 0 handlers/profile.py | 143 +++++++++++++++++-- handlers/start.py | 13 +- key_management.py | 8 +- 9 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 handlers/__pycache__/pay.cpython-310.pyc create mode 100644 handlers/pay.py diff --git a/bot.py b/bot.py index e284f260..1d5f2ec8 100644 --- a/bot.py +++ b/bot.py @@ -8,7 +8,7 @@ storage = MemoryStorage() dp = Dispatcher(bot=bot, storage=storage) router = Router() -from handlers import start, profile, keys, stats, expiry, balance +from handlers import start, profile, keys, stats, expiry, balance, pay from key_management import router as key_management_router @@ -21,5 +21,7 @@ dp.include_router(expiry.router) dp.include_router(balance.router) dp.include_router(key_management_router) + + async def on_startup(dispatcher: Dispatcher): await login_with_credentials() diff --git a/database.py b/database.py index 5d9ab340..68e7752d 100644 --- a/database.py +++ b/database.py @@ -81,3 +81,12 @@ async def get_balance(tg_id: int) -> str: async with db.execute("SELECT balance FROM connections WHERE tg_id = ?", (tg_id,)) as cursor: record = await cursor.fetchone() return record[0] if record else "Неизвестно" + +async def update_balance(tg_id: int, amount: float): + async with aiosqlite.connect(DATABASE_PATH) as db: + await db.execute(''' + UPDATE connections + SET balance = balance + ? + WHERE tg_id = ? + ''', (amount, tg_id)) + await db.commit() \ No newline at end of file diff --git a/handlers/__pycache__/pay.cpython-310.pyc b/handlers/__pycache__/pay.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9a2f72c1cfb6670e26e074bd6e2a906c1b194f3 GIT binary patch literal 132 zcmd1j<>g`k0_F=Z(?IlN5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!Hzenx(7s(x8c zVv4>?YMFj;eolUTQhtekMq*w{PHIuHenDcTUP0w84x8Nkl+v73JCFgzOhAH#0RYEO B8+!l% literal 0 HcmV?d00001 diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index 697ac76fc72f185bdc859791be8a823d0a4eb587..44a606fab2600ad127eab1506d5ecb8307dfb4b8 100644 GIT binary patch literal 6487 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 delta 1122 zcmYjQO>Epm6rML8|NrbyLz|@qTF@#O5`<7ur6^Qd3ROTVr6dx<0%o)i?PH;oLA<=}fH%QphH}Adqd++<+{PxDvrZ;w7 z3(4>IU;fxK&HLWDyWdEUA__4|AbZ zR%0u$#={7kGdeK0rZ(HKt8!Q1c#|0ks%xx2VmWEkc(Z>fR#<5VWbf zfqd`JDmx#SYPNfeO-k*f@lM9LkHy!pd_%?;Ml9=x!z}uO^>4D>K1;$VF6}Bh2;(pv zG9OBVh8fGEgnb|#>*~g}E8{d{cQWa@@ANi%Z}&Fd?SI(YTo*@X2PT3kFSO(wk=sH! z!th7LJ%N?i&y>bm93}%AUbbak6D+|(yeS?l7uUW6gua9vZKG`{a6RHe*?W*FIX-~* zB#Y~)AUWaRW@M^n+6*qDdGx_`u|)h!-9pQ?sWUr?7J9Dp_j8>p9aI>ZIdyY`;+q7{B6?RH!v#=>D+>cS(At-C_{tT#)>g~C-m3t4-BV5?xGxfqt zcJ9*lA)eO(C`Z$0{>#78T}pUL%+%|PT#|$KTHQ}py2(nHp6{-&cCW2=H^g6RM_kZ; zSe9CnN|VwZhH*Ryhd28t^f#h3j?!A*!8qh}=c@4ap17}n1->|Ayt<;vqf0Z4#*^D| zl(AADj?WTX({y4B~wQ8-TF~1QtMp)8HIqs60d38h+*P zmybOQV+`aNV{(KQ@)Q&8AAIZ>9$1)Q0%YIA7Bs=w$49408~B^@VEZxX`eh&xXgz<_RURP^W);V!&@%hg+$QhI}gmvi+q$p90We2s(-u>-ijcElVYQ@uK0}geC6NG zD}iDL_kgy7ksNSz(7mCg7oc~{qPgNWH6dJWW^BYW$Q+b8KVZoA8L-q!1qN8lVMxL{ zHgZ07iL|kqxhS7rVVRpX(KMe{3#4(yirE$_EN!jM$CuiryR=7EI{mZcLg~oUzIhfI zgr;NfZdLPSm5g5tF1dtC+!F97?`y&17A4?(w{5*dU^l9n2fKTy<4LNtu0Ai_Bb6lm iq51`MNYOuAtO#%CG{(tT?E!U(Bx^7UZE$F53w{9kmqn-m delta 397 zcmYjLze_?<6u#&BeyR8B6GEiWYD#?(-3WMPSgPgT6Va zrCDl>8k!qktA9a%Lo{@sG;}`r;5+9#=R1$&gHW5&2$Ge1|7zqWGz}+{_3bT&n9`1P zqSYuMj)$m9IJu`>Vd@YbfN30Bu?A*3Gj5b2RO2jsfUL-5)K6G2r};v;RH^M3^M|!k zVG389ecTT*!sr;95-i5zV|=L(P)ixWKx5_@mk>im1W5K&mKf;Fe13u~PPuXUn?KF^ zum0@w)}Q$={+k7Ydf%ZnQ)Ustd+m!_r!LOip0}d#w9w^-FeJ^@#c;0GyUsZmmLa0v zolMMl%j)h%;BYdM@U?;oCJ+FF@(1H5sW0Iah^(|!EYlmQm7`<3+c<5Cux!mWVY_`U rL^Ke3nLAFs$ro-BT>VF@fy3D8*vxJ7J&?+YNiq?El+4s*dJ=vC9p+x| diff --git a/handlers/pay.py b/handlers/pay.py new file mode 100644 index 00000000..e69de29b diff --git a/handlers/profile.py b/handlers/profile.py index ec859c61..d51e4d96 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -1,30 +1,35 @@ -from aiogram import types, Router -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup -from database import get_active_key_email, get_balance +from aiogram import Bot, Dispatcher, Router, types +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import State, StatesGroup from datetime import datetime import aiosqlite -from aiogram.fsm.context import FSMContext -from config import DATABASE_PATH +from config import DATABASE_PATH, ADMIN_ID +from database import get_balance, update_balance, has_active_key +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 == '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 get_active_key_email(tg_id) + # Получаем активный ключ + email = await has_active_key(tg_id) if email: async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute("SELECT expiry_time FROM connections WHERE tg_id = ? AND expiry_time > ?", - (tg_id, int(datetime.utcnow().timestamp() * 1000))) as cursor: + async with db.execute( + "SELECT expiry_time FROM connections WHERE tg_id = ? AND expiry_time > ?", + (tg_id, int(datetime.utcnow().timestamp() * 1000)) + ) as cursor: record = await cursor.fetchone() - if record: - expiry_time = record[0] - expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime("%Y-%m-%d %H:%M:%S") - else: - expiry_date = "Неизвестно" + 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 = ( @@ -36,9 +41,10 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta ) button_view_keys = InlineKeyboardButton(text='Мои ключи', callback_data='view_keys') - keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_view_keys]]) + button_replenish_balance = InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') + keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_view_keys], [button_replenish_balance]]) - profile_message += "Вы можете просмотреть ваши ключи ниже:" + profile_message += "Вы можете просмотреть ваши ключи или пополнить баланс ниже:" else: profile_message = "У вас нет активных ключей." keyboard = None @@ -47,5 +53,112 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta 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() diff --git a/handlers/start.py b/handlers/start.py index b245e72e..f2b3c05a 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,7 +1,6 @@ from aiogram import types, Router from aiogram.filters import Command from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, KeyboardButton -from config import ADMIN_CHAT_ID router = Router() @@ -13,6 +12,8 @@ async def start_command(message: types.Message): 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=[ [button_create_key], [button_view_stats], @@ -20,7 +21,13 @@ async def start_command(message: types.Message): [button_view_profile] ]) - main_menu_keyboard = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text="В начало")]], resize_keyboard=True) - + # Отправка приветственного сообщения await message.reply(welcome_text, reply_markup=keyboard) + + # Кнопка "В начало" для возвращения + main_menu_keyboard = ReplyKeyboardMarkup( + keyboard=[[KeyboardButton(text="В начало")]], + resize_keyboard=True + ) + await message.answer("Чтобы вернуться в начало, нажмите кнопку ниже:", reply_markup=main_menu_keyboard) diff --git a/key_management.py b/key_management.py index e99727aa..3709931b 100644 --- a/key_management.py +++ b/key_management.py @@ -15,7 +15,6 @@ import re from bot import dp from handlers.start import start_command from bot import bot -from handlers.stats import session router = Router() @@ -44,6 +43,10 @@ async def process_callback_create_key(callback_query: types.CallbackQuery, state async def handle_text(message: types.Message, state: FSMContext): current_state = await state.get_state() + if message.text == "/start": + await start_command(message) + return + if message.text == "В начало": await start_command(message) return @@ -88,7 +91,8 @@ async def handle_text(message: types.Message, state: FSMContext): async def handle_admin_confirmation(callback_query: CallbackQuery, state: FSMContext): if callback_query.message.chat.id != int(ADMIN_CHAT_ID): return - + + session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) data = callback_query.data.split('_', 3) tg_id = int(data[2]) key_name = data[3]