import logging from aiogram import Router, F, Bot from aiogram.types import Message, CallbackQuery from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from config import ADMIN_IDS from database.db import ( get_active_products, get_stock_summary, add_gift_code, count_users, get_total_stats, get_pending_delivery_orders, get_available_code, mark_code_used, complete_order, update_product_price, get_all_user_ids, count_available_codes ) from keyboards.admin_kb import ( admin_panel_kb, admin_select_product_kb, admin_back_kb, admin_pending_order_kb, cancel_admin_action_kb ) logger = logging.getLogger(__name__) router = Router() # ─────────────────── FSM STATES ─────────────────── class AdminStates(StatesGroup): waiting_product_for_code = State() # Qaysi mahsulotga kod qo'shish waiting_gift_link = State() # Kodni kiritish waiting_broadcast_message = State() # Broadcast xabari waiting_product_for_price = State() # Narx o'zgartirish uchun mahsulot waiting_new_price = State() # Yangi narxni kiritish # ─────────────────── ADMIN CHECK DECORATOR ─────────────────── def admin_only(func): """Faqat adminlar uchun filtr.""" async def wrapper(event, **kwargs): user_id = event.from_user.id if hasattr(event, 'from_user') else None if user_id not in ADMIN_IDS: if isinstance(event, Message): await event.answer("🚫 Ruxsat yo'q!") elif isinstance(event, CallbackQuery): await event.answer("🚫 Faqat adminlar!", show_alert=True) return return await func(event, **kwargs) return wrapper # ─────────────────── ADMIN PANEL ─────────────────── @router.message(Command("admin")) async def cmd_admin(message: Message): """Admin paneli — faqat adminlar uchun.""" if message.from_user.id not in ADMIN_IDS: await message.answer("🚫 Bu buyruq faqat adminlar uchun!") return await message.answer( "🛠️ Admin Panel\n\nXohlagan amalni tanlang:", reply_markup=admin_panel_kb(), parse_mode="HTML" ) @router.callback_query(F.data == "admin_panel") async def show_admin_panel(callback: CallbackQuery, state: FSMContext): """Admin panelga qaytish.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return await state.clear() await callback.message.edit_text( "🛠️ Admin Panel\n\nXohlagan amalni tanlang:", reply_markup=admin_panel_kb(), parse_mode="HTML" ) await callback.answer() # ─────────────────── ZAXIRA HOLATI ─────────────────── @router.callback_query(F.data == "admin_stock") async def show_stock(callback: CallbackQuery): """Zaxira holati.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return stock = await get_stock_summary() lines = ["📦 Zaxira Holati:\n"] for item in stock: status = "✅" if item["available"] > 0 else "❌" lines.append( f"{status} {item['emoji']} {item['name']}\n" f" ⭐ Narx: {item['stars_price']} Stars\n" f" 📌 Mavjud: {item['available']} ta | " f"Ishlatilgan: {item['used']} ta | Jami: {item['total']} ta\n" ) await callback.message.edit_text( "\n".join(lines), reply_markup=admin_back_kb(), parse_mode="HTML" ) await callback.answer() # ─────────────────── SOVGʻA LINK QO'SHISH ─────────────────── @router.callback_query(F.data == "admin_add_code") async def admin_add_code_start(callback: CallbackQuery, state: FSMContext): """Sovg'a link qo'shish — mahsulot tanlash.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return products = await get_active_products() await callback.message.edit_text( "➕ Sovg'a Link Qo'shish\n\nQaysi mahsulot uchun link qo'shmoqchisiz?", reply_markup=admin_select_product_kb(products, action="addcode"), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_product_for_code) await callback.answer() @router.callback_query(F.data.startswith("addcode_"), AdminStates.waiting_product_for_code) async def admin_add_code_product_selected(callback: CallbackQuery, state: FSMContext): """Mahsulot tanlandi — link kiritishni kutish.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return product_id = int(callback.data.split("_")[1]) await state.update_data(product_id=product_id) await state.set_state(AdminStates.waiting_gift_link) await callback.message.edit_text( "🔗 Gift Linkni Yuboring\n\n" "Fragment yoki Telegram Premium sovg'a linkini yuboring.\n" "Masalan: https://t.me/premium?gift=...\n\n" "Bir nechta link qo'shmoqchi bo'lsangiz, har birini alohida xabar sifatida yuboring.\n" "Tugatgach, ❌ Bekor Qilish tugmasini bosing.", reply_markup=cancel_admin_action_kb(), parse_mode="HTML" ) await callback.answer() @router.message(AdminStates.waiting_gift_link) async def admin_receive_gift_link(message: Message, state: FSMContext): """Admin tomonidan yuborilgan gift linkni saqlash.""" if message.from_user.id not in ADMIN_IDS: return data = await state.get_data() product_id = data.get("product_id") gift_link = message.text.strip() # URL tekshirish (oddiy) if not gift_link.startswith("https://"): await message.answer( "⚠️ Noto'g'ri format! Link https:// bilan boshlanishi kerak.\n" "Qaytadan yuboring:", parse_mode="HTML", reply_markup=cancel_admin_action_kb() ) return success = await add_gift_code(product_id=product_id, gift_link=gift_link) available = await count_available_codes(product_id) if success: await message.answer( f"✅ Link muvaffaqiyatli qo'shildi!\n\n" f"📦 Ushbu mahsulot uchun mavjud linklar soni: {available} ta\n\n" f"Yana link qo'shmoqchi bo'lsangiz, yuboring:", parse_mode="HTML", reply_markup=cancel_admin_action_kb() ) else: await message.answer( f"⚠️ Bu link allaqachon bazada mavjud! Boshqa link yuboring:", reply_markup=cancel_admin_action_kb() ) # ─────────────────── STATISTIKA ─────────────────── @router.callback_query(F.data == "admin_stats") async def show_stats(callback: CallbackQuery): """Umumiy statistika.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return stats = await get_total_stats() user_count = await count_users() text = ( "📊 Bot Statistikasi\n\n" f"👥 Jami foydalanuvchilar: {user_count} ta\n\n" f"✅ Bajarilgan buyurtmalar: {stats['total_completed']} ta\n" f"⭐ Jami Stars tushumi: {stats['total_stars']:,}\n" f"⏳ Yetkazilishi kutilayotgan: {stats['pending_delivery']} ta\n\n" ) # Zaxira holati stock = await get_stock_summary() text += "📦 Zaxira:\n" for item in stock: icon = "✅" if item["available"] > 0 else "❌" text += f" {icon} {item['emoji']} {item['name']}: {item['available']} ta\n" await callback.message.edit_text( text, reply_markup=admin_back_kb(), parse_mode="HTML" ) await callback.answer() # ─────────────────── KUTILAYOTGAN BUYURTMALAR ─────────────────── @router.callback_query(F.data == "admin_pending") async def show_pending_orders(callback: CallbackQuery): """Yetkazilishi kutilayotgan buyurtmalar.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return orders = await get_pending_delivery_orders() if not orders: await callback.message.edit_text( "✅ Kutilayotgan buyurtmalar yo'q!\n\nHamma buyurtmalar bajarilgan.", reply_markup=admin_back_kb(), parse_mode="HTML" ) await callback.answer() return for order in orders: username_str = f"@{order['username']}" if order['username'] else "username_yoq" await callback.message.answer( f"⏳ Kutilayotgan Buyurtma #{order['id']}\n\n" f"👤 {order['full_name']} ({username_str})\n" f"🆔 User ID: {order['user_id']}\n" f"📦 Mahsulot: {order['product_name']}\n" f"⭐ To'langan: {order['stars_amount']} Stars\n" f"📅 Sana: {order['created_at'][:16]}", reply_markup=admin_pending_order_kb( order_id=order["id"], user_id=order["user_id"], product_id=order["product_id"] ), parse_mode="HTML" ) await callback.answer() @router.callback_query(F.data.startswith("send_pending_")) async def send_pending_gift(callback: CallbackQuery, bot: Bot): """Kutilayotgan buyurtmaga link yuborish.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return # Callback data: send_pending_{order_id}_{user_id}_{product_id} # parts[0]=send, parts[1]=pending, parts[2]=order_id, parts[3]=user_id, parts[4]=product_id parts = callback.data.split("_") order_id = int(parts[2]) user_id = int(parts[3]) product_id = int(parts[4]) code = await get_available_code(product_id) if not code: await callback.answer( "❌ Zaxirada bu mahsulot uchun link yo'q! Avval link qo'shing.", show_alert=True ) return await mark_code_used(code_id=code["id"], user_id=user_id, order_id=order_id) await complete_order( order_id=order_id, gift_code_id=code["id"], charge_id="manual_delivery" ) # Foydalanuvchiga link yuborish try: await bot.send_message( user_id, f"🎉 Sizning Premium Sovg'angiz Tayyor!\n\n" f"Buyurtma ID: #{order_id}\n\n" f"🔗 Premium linkingiz:\n{code['gift_link']}\n\n" f"⚠️ Ushbu link bir marta ishlatiladi!", parse_mode="HTML", disable_web_page_preview=True ) await callback.message.edit_text( f"✅ Link muvaffaqiyatli yuborildi!\n\n" f"Buyurtma #{order_id} bajarildi.", reply_markup=admin_back_kb(), parse_mode="HTML" ) await callback.answer("✅ Yuborildi!") except Exception as e: logger.error(f"send_pending_gift xatosi: {e}") await callback.answer( f"❌ Xabar yuborishda xato: {e}", show_alert=True ) # ─────────────────── BROADCAST ─────────────────── @router.callback_query(F.data == "admin_broadcast") async def start_broadcast(callback: CallbackQuery, state: FSMContext): """Broadcast boshlash.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return user_count = await count_users() await callback.message.edit_text( f"📢 Xabar Yuborish (Broadcast)\n\n" f"Jami foydalanuvchilar: {user_count} ta\n\n" f"Barcha foydalanuvchilarga yuboriladigan xabarni yozing:\n" f"(HTML formatida yozishingiz mumkin: bold, italic, code)", reply_markup=cancel_admin_action_kb(), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_broadcast_message) await callback.answer() @router.message(AdminStates.waiting_broadcast_message) async def send_broadcast(message: Message, state: FSMContext, bot: Bot): """Broadcast xabarini barcha foydalanuvchilarga yuborish.""" if message.from_user.id not in ADMIN_IDS: return await state.clear() user_ids = await get_all_user_ids() total = len(user_ids) success_count = 0 fail_count = 0 status_msg = await message.answer( f"📤 Yuborilmoqda... 0/{total}" ) for i, uid in enumerate(user_ids, 1): try: await bot.send_message(uid, message.text, parse_mode="HTML") success_count += 1 except Exception: fail_count += 1 # Har 20 ta foydalanuvchida status yangilash if i % 20 == 0: try: await status_msg.edit_text(f"📤 Yuborilmoqda... {i}/{total}") except Exception: pass await status_msg.edit_text( f"✅ Broadcast yakunlandi!\n\n" f"📤 Yuborildi: {success_count} ta\n" f"❌ Xato: {fail_count} ta\n" f"📊 Jami: {total} ta", parse_mode="HTML", reply_markup=admin_back_kb() ) # ─────────────────── NARX O'ZGARTIRISH ─────────────────── @router.callback_query(F.data == "admin_prices") async def admin_prices_start(callback: CallbackQuery, state: FSMContext): """Narxlarni o'zgartirish — mahsulot tanlash.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return products = await get_active_products() await callback.message.edit_text( "💰 Narxni O'zgartirish\n\nQaysi mahsulot narxini o'zgartirmoqchisiz?", reply_markup=admin_select_product_kb(products, action="setprice"), parse_mode="HTML" ) await state.set_state(AdminStates.waiting_product_for_price) await callback.answer() @router.callback_query(F.data.startswith("setprice_"), AdminStates.waiting_product_for_price) async def admin_price_product_selected(callback: CallbackQuery, state: FSMContext): """Narx o'zgartirish uchun mahsulot tanlandi.""" if callback.from_user.id not in ADMIN_IDS: await callback.answer("🚫 Ruxsat yo'q!", show_alert=True) return product_id = int(callback.data.split("_")[1]) await state.update_data(product_id=product_id) await state.set_state(AdminStates.waiting_new_price) products = await get_active_products() product = next((p for p in products if p["id"] == product_id), None) if not product: await callback.answer("❌ Mahsulot topilmadi!", show_alert=True) await state.clear() return await callback.message.edit_text( f"💰 {product['name']}\n\n" f"Joriy narx: {product['stars_price']} ⭐ Stars\n\n" f"Yangi narxni (faqat raqam) yuboring:", reply_markup=cancel_admin_action_kb(), parse_mode="HTML" ) await callback.answer() @router.message(AdminStates.waiting_new_price) async def admin_set_new_price(message: Message, state: FSMContext): """Yangi narxni saqlash.""" if message.from_user.id not in ADMIN_IDS: return if not message.text.isdigit(): await message.answer( "⚠️ Faqat raqam kiriting! Masalan: 750", parse_mode="HTML", reply_markup=cancel_admin_action_kb() ) return new_price = int(message.text) if new_price < 1: await message.answer("⚠️ Narx 1 dan katta bo'lishi kerak!") return data = await state.get_data() product_id = data.get("product_id") await update_product_price(product_id, new_price) await state.clear() await message.answer( f"✅ Narx muvaffaqiyatli o'zgartirildi!\n\n" f"Yangi narx: {new_price} ⭐ Stars", parse_mode="HTML", reply_markup=admin_back_kb() )