diff --git a/bot.py b/bot.py index 75f607c6..991c9bbd 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,7 @@ from aiogram import Bot, Dispatcher, Router from aiogram.fsm.storage.memory import MemoryStorage -from config import API_TOKEN, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, YOOKASSA_ENABLE +from config import API_TOKEN, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE from middlewares.logging import UserActivityMiddleware bot = Bot(token=API_TOKEN) @@ -9,10 +9,10 @@ storage = MemoryStorage() dp = Dispatcher(bot=bot, storage=storage) router = Router() -from handlers import commands, notifications, profile, start +from handlers import commands, notifications, pay, profile, start from handlers.admin import admin, admin_panel, user_editor from handlers.keys import key_management, keys -from handlers.payment import cryprobot_pay, freekassa_pay, yookassa_pay +from handlers.payments import cryprobot_pay, freekassa_pay, stars_pay, yookassa_pay dp.include_router(admin.router) dp.include_router(admin_panel.router) @@ -22,12 +22,15 @@ dp.include_router(start.router) dp.include_router(profile.router) dp.include_router(keys.router) dp.include_router(key_management.router) +dp.include_router(pay.router) if YOOKASSA_ENABLE: dp.include_router(yookassa_pay.router) if FREEKASSA_ENABLE: dp.include_router(freekassa_pay.router) if CRYPTO_BOT_ENABLE: dp.include_router(cryprobot_pay.router) +if STARS_ENABLE: + dp.include_router(stars_pay.router) dp.include_router(notifications.router) dp.message.middleware(UserActivityMiddleware()) diff --git a/handlers/admin/admin.py b/handlers/admin/admin.py index 9022880d..f8187ccf 100644 --- a/handlers/admin/admin.py +++ b/handlers/admin/admin.py @@ -14,14 +14,16 @@ async def cmd_add_balance(message: types.Message): amount = float(amount) if not await check_connection_exists(int(client_id)): - await message.reply(f"Клиент с ID {client_id} не найден.") + await message.reply(f"❌ Клиент с ID {client_id} не найден в базе данных.") return await add_balance_to_client(int(client_id), amount) - await message.reply(f"Баланс клиента {client_id} увеличен на {amount} у.е.") + await message.reply(f"✅ Баланс клиента {client_id} успешно пополнен на {amount}") except ValueError: await message.reply( - "Пожалуйста, используйте формат: /add_balance " + "❓ Неверный формат команды!\n" + "Пожалуйста, используйте следующий шаблон:\n" + "/add_balance <сумма пополнения>" ) except Exception as e: - await message.reply(f"Произошла ошибка: {e}") + await message.reply(f"🚨 Произошла непредвиденная ошибка: {e}") diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index cd12fca4..ec021440 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -28,23 +28,23 @@ async def handle_admin_command(message: types.Message): builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( - text="Статистика пользователей", callback_data="user_stats" + text="📊 Статистика пользователей", callback_data="user_stats" ) ) builder.row( - InlineKeyboardButton(text="Редактор пользователей", callback_data="user_editor") + InlineKeyboardButton(text="👥 Управление пользователями", callback_data="user_editor") ) builder.row( InlineKeyboardButton( - text="Отправить сообщение всем клиентам", callback_data="send_to_alls" + text="📢 Массовая рассылка", callback_data="send_to_alls" ) ) - builder.row(InlineKeyboardButton(text="Создать бэкап", callback_data="backups")) + builder.row(InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups")) builder.row( - InlineKeyboardButton(text="Перезапустить бота", callback_data="restart_bot") + InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot") ) await bot.send_message( - message.chat.id, "Панель администратора.", reply_markup=builder.as_markup() + message.chat.id, "🤖 Панель администратора", reply_markup=builder.as_markup() ) @@ -63,17 +63,17 @@ async def user_stats_menu(callback_query: CallbackQuery): expired_keys = total_keys - active_keys stats_message = ( - f"🔹 Общая статистика пользователей:\n" - f"• Всего пользователей: {total_users}\n" - f"• Всего ключей: {total_keys}\n" - f"• Всего рефералов: {total_referrals}\n" - f"• Активные ключи: {active_keys}\n" - f"• Истекшие ключи: {expired_keys}" + f"📈 Подробная статистика проекта:\n\n" + f"👤 Зарегистрированных пользователей: {total_users}\n" + f"🔑 Всего сгенерированных ключей: {total_keys}\n" + f"🤝 Привлеченных рефералов: {total_referrals}\n" + f"✅ Действующих ключей: {active_keys}\n" + f"❌ Просроченных ключей: {expired_keys}" ) builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="Назад", callback_data="back_to_admin_menu") + InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="back_to_admin_menu") ) await callback_query.message.edit_text( @@ -92,9 +92,9 @@ async def handle_send_to_all(callback_query: CallbackQuery, state: FSMContext): @router.callback_query(F.data == "backups", IsAdminFilter()) async def handle_backup(message: Message): - await message.answer("Запускаю бэкап базы данных...") + await message.answer("💾 Инициализация резервного копирования базы данных...") await backup_database() - await message.answer("Бэкап завершен и отправлен админу.") + await message.answer("✅ Резервная копия успешно создана и отправлена администратору.") @router.callback_query(F.data == "restart_bot", IsAdminFilter) @@ -106,10 +106,10 @@ async def handle_restart(callback_query: CallbackQuery): capture_output=True, text=True, ) - await callback_query.message.answer("Бот успешно перезапущен.") + await callback_query.message.answer("🔄 Бот успешно перезапущен.") except subprocess.CalledProcessError as e: await callback_query.message.answer( - f"Бот будет перезапущен через 30 секунд {e.stderr}" + f"⚠️ Перезагрузка бота будет выполнена через 30 секунд. Детали: {e.stderr}" ) @@ -118,15 +118,15 @@ async def user_editor_menu(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( - text="Поиск по имени ключа", callback_data="search_by_key_name" + text="🔍 Поиск по названию ключа", callback_data="search_by_key_name" ) ) builder.row( - InlineKeyboardButton(text="Поиск по tg_id", callback_data="search_by_tg_id") + InlineKeyboardButton(text="🆔 Поиск по Telegram ID", callback_data="search_by_tg_id") ) - builder.row(InlineKeyboardButton(text="Назад", callback_data="back_to_admin_menu")) + builder.row(InlineKeyboardButton(text="🔙 Вернуться назад", callback_data="back_to_admin_menu")) await callback_query.message.edit_text( - "Выберите метод поиска:", reply_markup=builder.as_markup() + "👇 Выберите способ поиска пользователя:", reply_markup=builder.as_markup() ) @@ -141,24 +141,23 @@ async def back_to_admin_menu(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( - text="Статистика пользователей", callback_data="user_stats" + text="📊 Статистика пользователей", callback_data="user_stats" ) ) builder.row( - InlineKeyboardButton(text="Редактор пользователей", callback_data="user_editor") + InlineKeyboardButton(text="👥 Управление пользователями", callback_data="user_editor") ) builder.row( InlineKeyboardButton( - text="Отправить сообщение всем клиентам", - callback_data="send_to_alls", + text="📢 Массовая рассылка", callback_data="send_to_alls" ) ) - builder.row(InlineKeyboardButton(text="Создать бэкап", callback_data="backups")) + builder.row(InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups")) builder.row( - InlineKeyboardButton(text="Перезапустить бота", callback_data="restart_bot") + InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot") ) await bot.send_message( - tg_id, "Панель администратора", reply_markup=builder.as_markup() + tg_id, "🤖 Панель администратора", reply_markup=builder.as_markup() ) diff --git a/handlers/admin/user_editor.py b/handlers/admin/user_editor.py index 79bc160b..da27eb28 100644 --- a/handlers/admin/user_editor.py +++ b/handlers/admin/user_editor.py @@ -28,7 +28,7 @@ class UserEditorState(StatesGroup): @router.callback_query(F.data == "search_by_tg_id") async def prompt_tg_id(callback_query: CallbackQuery, state: FSMContext): - await callback_query.message.edit_text("Введите tg_id клиента:") + await callback_query.message.edit_text("🔍 Введите Telegram ID клиента:") await state.set_state(UserEditorState.waiting_for_tg_id) @@ -73,10 +73,10 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext): ) user_info = ( - f"Информация о пользователе:\n" - f"Баланс: {balance}\n" - f"Количество рефералов: {referral_count}\n" - f"Ключи (для редактирования нажмите на ключ):" + f"📊 Информация о пользователе:\n" + f"💰 Баланс: {balance}\n" + f"👥 Количество рефералов: {referral_count}\n" + f"🔑 Ключи (для редактирования нажмите на ключ):" ) await message.reply(user_info, reply_markup=keyboard, parse_mode="HTML") await state.set_state(UserEditorState.displaying_user_info) @@ -90,7 +90,7 @@ async def process_balance_change(callback_query: CallbackQuery, state: FSMContex tg_id = int(callback_query.data.split("_")[2]) await state.update_data(tg_id=tg_id) - await callback_query.message.edit_text("Введите новую сумму баланса:") + await callback_query.message.edit_text("💸 Введите новую сумму баланса:") await callback_query.answer() await state.set_state(UserEditorState.waiting_for_new_balance) @@ -99,7 +99,7 @@ async def process_balance_change(callback_query: CallbackQuery, state: FSMContex async def handle_new_balance_input(message: types.Message, state: FSMContext): if not message.text.isdigit() or int(message.text) < 0: await message.reply( - "Пожалуйста, введите корректную сумму для изменения баланса." + "❌ Пожалуйста, введите корректную сумму для изменения баланса." ) return @@ -113,7 +113,7 @@ async def handle_new_balance_input(message: types.Message, state: FSMContext): "UPDATE connections SET balance = $1 WHERE tg_id = $2", new_balance, tg_id ) - response_message = f"Баланс успешно изменен на {new_balance}." + response_message = f"✅ Баланс успешно изменен на {new_balance}." back_button = InlineKeyboardButton( text="Назад в меню админа", callback_data="back_to_user_editor" @@ -215,7 +215,7 @@ async def process_key_edit(callback_query: CallbackQuery): @router.callback_query(F.data == "search_by_key_name") async def prompt_key_name(callback_query: CallbackQuery, state: FSMContext): - await callback_query.message.edit_text("Введите имя ключа:") + await callback_query.message.edit_text("🔑 Введите имя ключа:") await state.set_state(UserEditorState.waiting_for_key_name) @@ -236,7 +236,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): ) if not user_records: - await message.reply("Пользователь с указанным именем ключа не найден.") + await message.reply("🚫 Пользователь с указанным именем ключа не найден.") await state.clear() return @@ -256,10 +256,10 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): ) response_messages.append( - f"Ключ:
{key}
\n" - f"Дата истечения: {expiry_date}\n" - f"Баланс пользователя: {balance}\n" - f"Сервер: {server_name}" + f"🔑 Ключ:
{key}
\n" + f"⏰ Дата истечения: {expiry_date}\n" + f"💰 Баланс пользователя: {balance}\n" + f"🌐 Сервер: {server_name}" ) change_expiry_button = InlineKeyboardButton( @@ -292,7 +292,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): async def prompt_expiry_change(callback_query: CallbackQuery, state: FSMContext): email = callback_query.data.split("|")[1] await callback_query.message.edit_text( - f"Введите новое время истечения для ключа {email} в формате YYYY-MM-DD HH:MM:SS:", + f"⏳ Введите новое время истечения для ключа {email} в формате YYYY-MM-DD HH:MM:SS:", parse_mode="HTML", ) await state.update_data(email=email) @@ -345,7 +345,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext): await update_key_expiry(client_id, expiry_time) - response_message = f"Время истечения ключа для клиента {client_id} ({email}) успешно обновлено на всех серверах." + response_message = f"✅ Время истечения ключа для клиента {client_id} ({email}) успешно обновлено на всех серверах." back_button = InlineKeyboardButton( text="Назад", callback_data="back_to_user_editor" @@ -360,7 +360,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext): await conn.close() except ValueError: - await message.reply("Пожалуйста, используйте формат: YYYY-MM-DD HH:MM:SS.") + await message.reply("❌ Пожалуйста, используйте формат: YYYY-MM-DD HH:MM:SS.") except Exception as e: await message.reply(f"Произошла ошибка: {e}") @@ -403,7 +403,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): ) await bot.edit_message_text( - "Вы уверены, что хотите удалить ключ?", + "❓ Вы уверены, что хотите удалить ключ?", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=confirmation_keyboard, @@ -429,7 +429,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): if record: email = record["email"] - response_message = "Ключ успешно удален." + response_message = "✅ Ключ успешно удален." back_button = types.InlineKeyboardButton( text="Назад", callback_data="view_keys" ) @@ -453,7 +453,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): reply_markup=keyboard, ) else: - response_message = "Ключ не найден или уже удален." + response_message = "🚫 Ключ не найден или уже удален." back_button = types.InlineKeyboardButton( text="Назад", callback_data="view_keys" ) diff --git a/handlers/commands.py b/handlers/commands.py index 0df37208..c055e197 100644 --- a/handlers/commands.py +++ b/handlers/commands.py @@ -11,7 +11,7 @@ from bot import bot from config import DATABASE_URL from handlers.admin.admin import cmd_add_balance from handlers.keys.key_management import handle_key_name_input -from handlers.payment.yookassa_pay import ReplenishBalanceState, process_custom_amount_input +from handlers.payments.yookassa_pay import ReplenishBalanceState, process_custom_amount_input from handlers.profile import process_callback_view_profile from handlers.start import start_command from handlers.texts import TRIAL @@ -30,9 +30,9 @@ class Form(StatesGroup): async def backup_command(message: Message): from backup import backup_database - await message.answer("Запускаю бэкап базы данных...") + await message.answer("🔄 Инициализация резервного копирования базы данных...") await backup_database() - await message.answer("Бэкап завершен и отправлен админу.") + await message.answer("✅ Бэкап базы данных успешно завершен и отправлен администратору.") @router.message(Command("start")) @@ -62,34 +62,38 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): ) if records: + success_count = 0 + error_count = 0 + blocked_count = 0 + for record in records: tg_id = record["tg_id"] trial_message = TRIAL try: await bot.send_message(chat_id=tg_id, text=trial_message) + success_count += 1 except Exception as e: if "Forbidden: bot was blocked by the user" in str(e): - logger.info( - f"Бот заблокирован пользователем с tg_id: {tg_id}" - ) + blocked_count += 1 + logger.info(f"🚫 Бот заблокирован пользователем с tg_id: {tg_id}") else: - logger.error( - f"Ошибка при отправке сообщения пользователю {tg_id}: {e}" - ) + error_count += 1 + logger.error(f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}") await message.answer( - "Сообщения о пробном периоде отправлены всем пользователям с не использованным ключом." + f"📊 Результаты рассылки пробных периодов:\n" + f"✅ Успешно отправлено: {success_count}\n" + f"🚫 Заблокировано: {blocked_count}\n" + f"❌ Ошибок: {error_count}" ) else: - await message.answer( - "Нет пользователей с не использованными пробными ключами." - ) + await message.answer("📭 Нет пользователей с неиспользованными пробными ключами.") finally: await conn.close() except Exception as e: - await message.answer(f"Ошибка при отправке сообщений: {e}") + await message.answer(f"❗ Ошибка при отправке сообщений: {e}") @router.message(Command("send_to_all"), IsAdminFilter()) @@ -97,9 +101,8 @@ async def send_message_to_all_clients( message: types.Message, state: FSMContext, from_panel=False ): if from_panel: - await message.answer( - "Введите текст сообщения, который вы хотите отправить всем клиентам:" + "✍️ Введите текст сообщения, который вы хотите отправить всем клиентам:" ) await state.set_state(Form.waiting_for_message) @@ -115,19 +118,30 @@ async def process_message_to_all( conn = await asyncpg.connect(DATABASE_URL) tg_ids = await conn.fetch("SELECT tg_id FROM connections") + total_users = len(tg_ids) + success_count = 0 + error_count = 0 + for record in tg_ids: tg_id = record["tg_id"] try: await bot.send_message(chat_id=tg_id, text=text_message) + success_count += 1 except Exception as e: + error_count += 1 logger.error( - f"Ошибка при отправке сообщения пользователю {tg_id}: {e}. Пропускаем этого пользователя." + f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}" ) - await message.answer("Сообщение было отправлено всем клиентам.") + await message.answer( + f"📤 Рассылка завершена:\n" + f"👥 Всего пользователей: {total_users}\n" + f"✅ Успешно отправлено: {success_count}\n" + f"❌ Не доставлено: {error_count}" + ) except Exception as e: - logger.error(f"Ошибка при подключении к базе данных: {e}") - await message.answer("Произошла ошибка при отправке сообщения.") + logger.error(f"❗ Ошибка при подключении к базе данных: {e}") + await message.answer("❌ Произошла ошибка при отправке сообщения.") finally: await conn.close() diff --git a/handlers/donate.py b/handlers/donate.py new file mode 100644 index 00000000..e69de29b diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 82d7c9a6..2e219bb4 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -135,7 +135,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): if existing_key: await message.bot.send_message( tg_id, - "❌ Это имя уже используется. Пожалуйста, выберите другое имя для ключа.", + "❌ Упс! Это имя уже используется. Выберите другое уникальное название для ключа.", ) await state.set_state(Form.waiting_for_key_name) return @@ -168,7 +168,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) await message.bot.send_message( tg_id, - "❗️ Недостаточно средств на балансе для создания подписки на новое устройство.", + "💳 Недостаточно средств для создания подписки на новое устройство. Пополните баланс в личном кабинете.", reply_markup=keyboard, ) await state.clear() @@ -181,7 +181,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): public_link = f"{PUBLIC_LINK}{email}" button_profile = InlineKeyboardButton( - text="👤 Мой профиль", callback_data="view_profile" + text="👤 Личный кабинет", callback_data="view_profile" ) button_iphone = InlineKeyboardButton( text="🍏 Подключить", url=f"{APP_URL}/?url=v2raytun://import/{public_link}" @@ -210,7 +210,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): remaining_time = expiry_time - current_time days = remaining_time.days key_message = key_message_success( - public_link, f"Оставшееся время ключа: {days} день" + public_link, f"⏳ Осталось дней: {days} 📅" ) await message.bot.send_message( diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 1ad8819d..24c7efe1 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -52,8 +52,8 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons) response_message = ( - "Это ваши устройства:\n\n" - "Нажмите на имя устройства для управления его подпиской." + "🔑 Список ваших устройств\n\n" + "👆 Выберите устройство для управления подпиской:" ) await bot.delete_message( @@ -138,7 +138,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): time_left = expiry_date - current_date if time_left.total_seconds() <= 0: - days_left_message = "Ключ истек." + days_left_message = "🕒 Статус подписки:\n🔴 Истекла\nОсталось часов: 0" elif time_left.days > 0: days_left_message = f"Осталось дней: {time_left.days}" else: @@ -574,11 +574,14 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): balance = await get_balance(tg_id) if balance < cost: + replenish_button = types.InlineKeyboardButton( + text="Пополнить баланс", callback_data="pay" + ) view_profile = types.InlineKeyboardButton( - text="👤 Мой профиль", callback_data="view_profile" + text="👤 Личный кабинет", callback_data="view_profile" ) keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[[view_profile]] + inline_keyboard=[[replenish_button], [view_profile]] ) await bot.send_message( diff --git a/handlers/notifications.py b/handlers/notifications.py index 3ee57d13..ecdf56f4 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -106,7 +106,13 @@ async def notify_10h_keys( ], [ types.InlineKeyboardButton( - text="👤 Мой профиль", callback_data="view_profile" + text="💳 Пополнить баланс", + callback_data="pay", + ) + ], + [ + types.InlineKeyboardButton( + text="👤 Личный кабинет", callback_data="view_profile" ) ], ] @@ -178,7 +184,13 @@ async def notify_24h_keys( ], [ types.InlineKeyboardButton( - text="👤 Мой профиль", callback_data="view_profile" + text="💳 Пополнить баланс", + callback_data="pay", + ) + ], + [ + types.InlineKeyboardButton( + text="👤 Личный кабинет", callback_data="view_profile" ) ], ] @@ -236,9 +248,13 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: f"Время истечения ключа: {expiry_time} (дата: {expiry_date}), Текущее время: {current_date}, Оставшееся время: {time_left}" ) - message_expired = f"Ваша подписка {email} истекла и была удалена!\n\n Перейдите в профиль для создания нового ключа" + message_expired = ( + "❌ Ваша подписка {email} истекла и была удалена!\n\n" + "🔍 Перейдите в профиль для создания нового ключа.\n" + "💡 Не откладывайте подключение VPN!" + ) button_profile = types.InlineKeyboardButton( - text="👤 Мой профиль", callback_data="view_profile" + text="👤 Личный кабинет", callback_data="view_profile" ) keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[button_profile]]) diff --git a/handlers/pay.py b/handlers/pay.py new file mode 100644 index 00000000..1e158842 --- /dev/null +++ b/handlers/pay.py @@ -0,0 +1,65 @@ +from aiogram import F, Router +from aiogram.types import CallbackQuery, InlineKeyboardButton +from aiogram.utils.keyboard import InlineKeyboardBuilder + +from config import CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE +from database import get_trial +from handlers.start import send_welcome_message + +router = Router() + + +@router.callback_query(F.data == "pay") +async def handle_pay(callback_query: CallbackQuery): + await callback_query.message.delete() + builder = InlineKeyboardBuilder() + + if YOOKASSA_ENABLE: + builder.row( + InlineKeyboardButton( + text="💳 ЯКасса: быстрый перевод", + callback_data="pay_yookassa", + ) + ) + if FREEKASSA_ENABLE: + builder.row( + InlineKeyboardButton( + text="🌐 FreeKassa: множество способов", + callback_data="pay_freekassa", + ) + ) + if CRYPTO_BOT_ENABLE: + builder.row( + InlineKeyboardButton( + text="💰 CryptoBot: криптовалюта", + callback_data="pay_cryptobot", + ) + ) + if STARS_ENABLE: + builder.row( + InlineKeyboardButton( + text="⭐ Оплата Звездами", + callback_data="pay_stars", + ) + ) + + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) + + await callback_query.message.answer( + "💸 Выберите удобный способ пополнения баланса:\n\n" + "• Быстро и безопасно\n" + "• Поддержка разных платежных систем\n" + "• Моментальное зачисление средств 🚀", + parse_mode="HTML", + reply_markup=builder.as_markup(), + ) + + await callback_query.answer() + + +@router.callback_query(F.data == "back_to_menu") +async def handle_back_to_menu(callback_query: CallbackQuery): + await callback_query.message.delete() + trial_status = await get_trial(callback_query.from_user.id) + await send_welcome_message(callback_query.from_user.id, trial_status) + await callback_query.answer() diff --git a/handlers/payment/cryprobot_pay.py b/handlers/payments/cryprobot_pay.py similarity index 100% rename from handlers/payment/cryprobot_pay.py rename to handlers/payments/cryprobot_pay.py diff --git a/handlers/payment/freekassa_pay.py b/handlers/payments/freekassa_pay.py similarity index 100% rename from handlers/payment/freekassa_pay.py rename to handlers/payments/freekassa_pay.py diff --git a/handlers/payment/stars_pay.py b/handlers/payments/stars_pay.py similarity index 100% rename from handlers/payment/stars_pay.py rename to handlers/payments/stars_pay.py diff --git a/handlers/payment/yookassa_pay.py b/handlers/payments/yookassa_pay.py similarity index 100% rename from handlers/payment/yookassa_pay.py rename to handlers/payments/yookassa_pay.py diff --git a/handlers/profile.py b/handlers/profile.py index 0f168a73..77df7759 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -7,7 +7,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder from loguru import logger from bot import bot -from config import CHANNEL_URL, CRYPTO_BOT_ENABLE, FREEKASSA_ENABLE, STARS_ENABLE, YOOKASSA_ENABLE +from config import CHANNEL_URL from database import get_balance, get_key_count, get_referral_stats from handlers.texts import get_referral_link, invite_message_send, profile_message_send @@ -32,53 +32,32 @@ async def process_callback_view_profile( if key_count == 0: profile_message += ( - "\nНажмите ➕Устройство снизу, чтобы добавить устройство в VPN" + "\n🔧 Нажмите кнопку ➕ Устройство, чтобы настроить VPN-подключение" ) builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="📢 Наш канал", url=CHANNEL_URL)) builder.row( InlineKeyboardButton(text="➕ Устройство", callback_data="create_key"), - InlineKeyboardButton(text="📱 Мои устр-ва", callback_data="view_keys"), + InlineKeyboardButton(text="📱 Мои устройства", callback_data="view_keys"), ) - if YOOKASSA_ENABLE: - builder.row( - InlineKeyboardButton( - text="💳 Пополнить баланс ЯКассой", - callback_data="pay_yookassa", - ) - ) - if FREEKASSA_ENABLE: - builder.row( - InlineKeyboardButton( - text="💳 Пополнить баланс Freekassa", - callback_data="pay_freekassa", - ) - ) - if CRYPTO_BOT_ENABLE: - builder.row( - InlineKeyboardButton( - text="💳 Пополнить баланс CryptoBot", - callback_data="pay_cryptobot", - ) - ) - if STARS_ENABLE: - builder.row( - InlineKeyboardButton( - text="💳 Пополнить баланс Звездами", - callback_data="pay_stars", - ) - ) builder.row( - InlineKeyboardButton(text="👥 Пригласить", callback_data="invite"), + InlineKeyboardButton( + text="💳 Пополнить баланс", + callback_data="pay", + ) + ) + builder.row( + InlineKeyboardButton(text="👥 Пригласить друзей", callback_data="invite"), InlineKeyboardButton(text="📘 Инструкции", callback_data="instructions"), ) - builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_menu")) + builder.row(InlineKeyboardButton(text="⬅️ Главное меню", callback_data="back_to_menu")) try: await callback_query.message.delete() except Exception as e: - logger.error(f"Ошибка при удалении сообщения: {e}") + logger.error(f"❗ Ошибка при удалении сообщения: {e}") + if os.path.isfile(image_path): with open(image_path, "rb") as image_file: await bot.send_photo( @@ -97,7 +76,7 @@ async def process_callback_view_profile( ) except Exception as e: - await bot.send_message(chat_id, f"❗️ Ошибка при получении данных профиля: {e}") + await bot.send_message(chat_id, f"❗️ Не удалось загрузить профиль. Техническая ошибка: {e}") await callback_query.answer() @@ -112,7 +91,7 @@ async def invite_handler(callback_query: types.CallbackQuery): invite_message = invite_message_send(referral_link, referral_stats) builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="view_profile")) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) await callback_query.message.delete() diff --git a/handlers/start.py b/handlers/start.py index 94ef8540..a69b542d 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -25,13 +25,13 @@ async def send_welcome_message(chat_id: int, trial_status: int): InlineKeyboardButton(text="🔗 Подключить VPN", callback_data="connect_vpn") ) builder.row( - InlineKeyboardButton(text="👤 Мой профиль", callback_data="view_profile") + InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") ) builder.row( - InlineKeyboardButton(text="📞 Поддержка", url=SUPPORT_CHAT_URL), - InlineKeyboardButton(text="📢 Наш канал", url=CHANNEL_URL), + InlineKeyboardButton(text="📞 Техническая поддержка", url=SUPPORT_CHAT_URL), + InlineKeyboardButton(text="📢 Официальный канал", url=CHANNEL_URL), ) - builder.row(InlineKeyboardButton(text="🔒 О VPN", callback_data="about_vpn")) + builder.row(InlineKeyboardButton(text="🌐 О нашем VPN", callback_data="about_vpn")) if os.path.isfile(image_path): with open(image_path, "rb") as image_from_buffer: @@ -95,34 +95,35 @@ async def handle_connect_vpn(callback_query: CallbackQuery): await conn.close() key_message = ( - f"Ваш ключ доступа:\n
{trial_key_info['key']}
\n\n" - f"Инструкции:\n{INSTRUCTIONS_TRIAL}" + f"🔑 Ваш персональный ключ доступа:\n" + f"
{trial_key_info['key']}
\n\n" + f"📋 Быстрая инструкция по подключению:\n{INSTRUCTIONS_TRIAL}" ) builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="👤 Мой профиль", callback_data="view_profile") + InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") ) builder.row( InlineKeyboardButton( - text="🍏 Скачать", - url="https://apps.apple.com/ru/app/v2raytun/id6476628951", + text="🍏 Скачать для iOS", + url="https://apps.apple.com/ru/app/v2raytun/id6476628951" ), InlineKeyboardButton( - text="🤖 Скачать", - url="https://play.google.com/store/apps/details?id=com.v2raytun.android&hl=ru", + text="🤖 Скачать для Android", + url="https://play.google.com/store/apps/details?id=com.v2raytun.android&hl=ru" ), ) builder.row( InlineKeyboardButton( - text="🍏 Подключить", - url=f'{APP_URL}/?url=v2raytun://import/{trial_key_info["key"]}', + text="🍏 Подключить на iOS", + url=f'{APP_URL}/?url=v2raytun://import/{trial_key_info["key"]}' ), InlineKeyboardButton( - text="🤖 Подключить", - url=f'{APP_URL}/?url=v2raytun://import-sub?url={trial_key_info["key"]}', + text="🤖 Подключить на Android", + url=f'{APP_URL}/?url=v2raytun://import-sub?url={trial_key_info["key"]}' ), )