diff --git a/handlers/admin/stats/stats_handler.py b/handlers/admin/stats/stats_handler.py index 5ee43ab4..b2efe1df 100644 --- a/handlers/admin/stats/stats_handler.py +++ b/handlers/admin/stats/stats_handler.py @@ -121,7 +121,7 @@ async def handle_stats(callback_query: CallbackQuery, session: Any): f"├ 📆 За неделю: {total_payments_week} ₽\n" f"├ 📆 За месяц: {total_payments_month} ₽\n" f"└ 🏦 Всего: {total_payments_all_time} ₽\n\n" - f"🔥 Горящие лиды: {hot_leads_count} (платили, но не активировали ключи)\n\n" + f"🔥 Горящие лиды: {hot_leads_count} (платили, но не продлили)\n\n" f"⏱️ Последнее обновление: {update_time}" ) diff --git a/handlers/buttons.py b/handlers/buttons.py index c0032a80..7bd83484 100644 --- a/handlers/buttons.py +++ b/handlers/buttons.py @@ -45,14 +45,16 @@ CANCEL = "❌ Отмена" DOWNLOAD_IOS_BUTTON = "🍏 Скачать iOS" DOWNLOAD_ANDROID_BUTTON = "🤖 Скачать Android" +DOWNLOAD_MACOS_BUTTON = "🍏 Скачать macOS" +DOWNLOAD_PC_BUTTON = "💻 Скачать Windows" IMPORT_IOS = "🍏 Подключить" IMPORT_ANDROID = "🤖 Подключить" PC_BUTTON = "💻 Компьютеры" TV_BUTTON = "📺 Андроид TV" CONNECT_PHONE = "📱 Подключить телефон" CONNECT_DEVICE = "📲 Подключить устройство" -CONNECT_WINDOWS_BUTTON = "💻 Подключить Windows" -CONNECT_MACOS_BUTTON = "💻 Подключить MacOS" +CONNECT_WINDOWS_BUTTON = "💻 Подключить" +CONNECT_MACOS_BUTTON = "🍏 Подключить" ALIAS = "✏️" UNFREEZE = "🟢 Разморозить подписку" FREEZE = "🛑 Заморозить подписку" @@ -64,6 +66,8 @@ QR = "📷 Показать QR-код" IPHONE = "🍏 Айфон" ANDROID = "🤖 Андроид" PC = "💻 Компьютер" +PC_PC = "💻 Windows" +PC_MACOS = "🍏 macOS" TV = "📺 Телевизор" ROUTER = "📶 Роутер" MANUAL_INSTRUCTIONS = "📖 Ручная установка" diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index f436d7fc..22545e69 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -1,5 +1,4 @@ import os - from typing import Any from aiogram import F, Router @@ -10,20 +9,32 @@ from aiogram.types import ( ) from aiogram.utils.keyboard import InlineKeyboardBuilder -from config import CONNECT_MACOS, CONNECT_WINDOWS, SUPPORT_CHAT_URL +from config import ( + CONNECT_MACOS, + CONNECT_WINDOWS, + DOWNLOAD_MACOS, + DOWNLOAD_PC, + SUPPORT_CHAT_URL, +) from database import get_key_details from handlers.buttons import ( BACK, CONNECT_MACOS_BUTTON, CONNECT_WINDOWS_BUTTON, + DOWNLOAD_MACOS_BUTTON, + DOWNLOAD_PC_BUTTON, MAIN_MENU, + PC_MACOS, + PC_PC, SUPPORT, TV_CONTINUE, TV_INSTRUCTIONS, ) from handlers.texts import ( + CHOOSE_DEVICE_TEXT, CONNECT_TV_TEXT, INSTRUCTIONS, + INSTRUCTION_MACOS, INSTRUCTION_PC, KEY_MESSAGE, SUBSCRIPTION_DETAILS_TEXT, @@ -72,16 +83,54 @@ async def process_connect_pc(callback_query: CallbackQuery, session: Any): ) return + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=PC_PC, callback_data=f"windows_menu|{key_name}")) + builder.row(InlineKeyboardButton(text=PC_MACOS, callback_data=f"macos_menu|{key_name}")) + builder.row(InlineKeyboardButton(text=BACK, callback_data=f"view_key|{key_name}")) + + await edit_or_send_message( + target_message=callback_query.message, + text=CHOOSE_DEVICE_TEXT, + reply_markup=builder.as_markup(), + media_path=None, + ) + + +@router.callback_query(F.data.startswith("windows_menu|")) +async def process_windows_menu(callback_query: CallbackQuery, session: Any): + key_name = callback_query.data.split("|")[1] + record = await get_key_details(key_name, session) key = record["key"] key_message_text = KEY_MESSAGE.format(key) instruction_message = f"{key_message_text}{INSTRUCTION_PC}" builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=DOWNLOAD_PC_BUTTON, url=DOWNLOAD_PC)) builder.row(InlineKeyboardButton(text=CONNECT_WINDOWS_BUTTON, url=f"{CONNECT_WINDOWS}{key}")) + builder.row(InlineKeyboardButton(text=SUPPORT, url=SUPPORT_CHAT_URL)) + builder.row(InlineKeyboardButton(text=BACK, callback_data=f"connect_pc|{key_name}")) + + await edit_or_send_message( + target_message=callback_query.message, + text=instruction_message, + reply_markup=builder.as_markup(), + media_path=None, + ) + + +@router.callback_query(F.data.startswith("macos_menu|")) +async def process_macos_menu(callback_query: CallbackQuery, session: Any): + key_name = callback_query.data.split("|")[1] + record = await get_key_details(key_name, session) + key = record["key"] + key_message_text = KEY_MESSAGE.format(key) + instruction_message = f"{key_message_text}{INSTRUCTION_MACOS}" + + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=DOWNLOAD_MACOS_BUTTON, url=DOWNLOAD_MACOS)) builder.row(InlineKeyboardButton(text=CONNECT_MACOS_BUTTON, url=f"{CONNECT_MACOS}{key}")) builder.row(InlineKeyboardButton(text=SUPPORT, url=SUPPORT_CHAT_URL)) - builder.row(InlineKeyboardButton(text=BACK, callback_data=f"view_key|{key_name}")) - builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) + builder.row(InlineKeyboardButton(text=BACK, callback_data=f"connect_pc|{key_name}")) await edit_or_send_message( target_message=callback_query.message, @@ -123,5 +172,8 @@ async def process_continue_tv(callback_query: CallbackQuery, session: Any): builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) await edit_or_send_message( - target_message=callback_query.message, text=message_text, reply_markup=builder.as_markup(), media_path=None + target_message=callback_query.message, + text=message_text, + reply_markup=builder.as_markup(), + media_path=None, ) diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index f9d804de..2e85766f 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -87,6 +87,7 @@ from handlers.payments.robokassa_pay import handle_custom_amount_input from handlers.payments.yookassa_pay import process_custom_amount_input from handlers.texts import ( ANDROID_DESCRIPTION_TEMPLATE, + CHOOSE_DEVICE_TEXT, DELETE_KEY_CONFIRM_MSG, DISCOUNTS, FREEZE_SUBSCRIPTION_CONFIRM_MSG, @@ -420,7 +421,7 @@ async def handle_connect_device(callback_query: CallbackQuery): await edit_or_send_message( target_message=callback_query.message, - text="📲 Выберите устройство, которое хотите подключить:", + text=CHOOSE_DEVICE_TEXT, reply_markup=builder.as_markup(), media_path=None, ) diff --git a/handlers/profile.py b/handlers/profile.py index 85b1458d..82fe9ea3 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -294,6 +294,28 @@ async def inline_referral_handler(inline_query: InlineQuery): async def top_referrals_handler(callback_query: CallbackQuery): conn = await asyncpg.connect(DATABASE_URL) try: + user_referral_count = await conn.fetchval( + "SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", + callback_query.from_user.id + ) or 0 + + personal_block = "Твоё место в рейтинге:\n" + if user_referral_count > 0: + user_position = await conn.fetchval( + """ + SELECT COUNT(*) + 1 FROM ( + SELECT COUNT(*) as cnt + FROM referrals + GROUP BY referrer_tg_id + HAVING COUNT(*) > $1 + ) AS better_users + """, + user_referral_count + ) + personal_block += f"{user_position}. {callback_query.from_user.id} - {user_referral_count} чел." + else: + personal_block += "Ты еще не приглашал пользователей в проект." + top_referrals = await conn.fetch( """ SELECT referrer_tg_id, COUNT(*) as referral_count @@ -306,14 +328,13 @@ async def top_referrals_handler(callback_query: CallbackQuery): is_admin = callback_query.from_user.id in ADMIN_ID rows = "" - for i, row in enumerate(top_referrals, 1): tg_id = str(row["referrer_tg_id"]) count = row["referral_count"] display_id = tg_id if is_admin else f"{tg_id[:5]}*****" rows += f"{i}. {display_id} - {count} чел.\n" - text = TOP_REFERRALS_TEXT.format(rows=rows) + text = TOP_REFERRALS_TEXT.format(personal_block=personal_block, rows=rows) builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text=BACK, callback_data="invite"))