Spaces:
Runtime error
Runtime error
| 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 βββββββββββββββββββ | |
| 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( | |
| "π οΈ <b>Admin Panel</b>\n\nXohlagan amalni tanlang:", | |
| reply_markup=admin_panel_kb(), | |
| parse_mode="HTML" | |
| ) | |
| 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( | |
| "π οΈ <b>Admin Panel</b>\n\nXohlagan amalni tanlang:", | |
| reply_markup=admin_panel_kb(), | |
| parse_mode="HTML" | |
| ) | |
| await callback.answer() | |
| # βββββββββββββββββββ ZAXIRA HOLATI βββββββββββββββββββ | |
| 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 = ["π¦ <b>Zaxira Holati:</b>\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: <b>{item['available']}</b> 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 βββββββββββββββββββ | |
| 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( | |
| "β <b>Sovg'a Link Qo'shish</b>\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() | |
| 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( | |
| "π <b>Gift Linkni Yuboring</b>\n\n" | |
| "Fragment yoki Telegram Premium sovg'a linkini yuboring.\n" | |
| "Masalan: <code>https://t.me/premium?gift=...</code>\n\n" | |
| "Bir nechta link qo'shmoqchi bo'lsangiz, har birini alohida xabar sifatida yuboring.\n" | |
| "Tugatgach, <b>β Bekor Qilish</b> tugmasini bosing.", | |
| reply_markup=cancel_admin_action_kb(), | |
| parse_mode="HTML" | |
| ) | |
| await callback.answer() | |
| 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 <code>https://</code> 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"β <b>Link muvaffaqiyatli qo'shildi!</b>\n\n" | |
| f"π¦ Ushbu mahsulot uchun mavjud linklar soni: <b>{available}</b> 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 βββββββββββββββββββ | |
| 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 = ( | |
| "π <b>Bot Statistikasi</b>\n\n" | |
| f"π₯ Jami foydalanuvchilar: <b>{user_count}</b> ta\n\n" | |
| f"β Bajarilgan buyurtmalar: <b>{stats['total_completed']}</b> ta\n" | |
| f"β Jami Stars tushumi: <b>{stats['total_stars']:,}</b>\n" | |
| f"β³ Yetkazilishi kutilayotgan: <b>{stats['pending_delivery']}</b> ta\n\n" | |
| ) | |
| # Zaxira holati | |
| stock = await get_stock_summary() | |
| text += "π¦ <b>Zaxira:</b>\n" | |
| for item in stock: | |
| icon = "β " if item["available"] > 0 else "β" | |
| text += f" {icon} {item['emoji']} {item['name']}: <b>{item['available']}</b> ta\n" | |
| await callback.message.edit_text( | |
| text, | |
| reply_markup=admin_back_kb(), | |
| parse_mode="HTML" | |
| ) | |
| await callback.answer() | |
| # βββββββββββββββββββ KUTILAYOTGAN BUYURTMALAR βββββββββββββββββββ | |
| 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( | |
| "β <b>Kutilayotgan buyurtmalar yo'q!</b>\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"β³ <b>Kutilayotgan Buyurtma #{order['id']}</b>\n\n" | |
| f"π€ {order['full_name']} ({username_str})\n" | |
| f"π User ID: <code>{order['user_id']}</code>\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() | |
| 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"π <b>Sizning Premium Sovg'angiz Tayyor!</b>\n\n" | |
| f"Buyurtma ID: #{order_id}\n\n" | |
| f"π Premium linkingiz:\n{code['gift_link']}\n\n" | |
| f"β οΈ <i>Ushbu link bir marta ishlatiladi!</i>", | |
| parse_mode="HTML", | |
| disable_web_page_preview=True | |
| ) | |
| await callback.message.edit_text( | |
| f"β <b>Link muvaffaqiyatli yuborildi!</b>\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 βββββββββββββββββββ | |
| 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"π’ <b>Xabar Yuborish (Broadcast)</b>\n\n" | |
| f"Jami foydalanuvchilar: <b>{user_count}</b> ta\n\n" | |
| f"Barcha foydalanuvchilarga yuboriladigan xabarni yozing:\n" | |
| f"(HTML formatida yozishingiz mumkin: <b>bold</b>, <i>italic</i>, <code>code</code>)", | |
| reply_markup=cancel_admin_action_kb(), | |
| parse_mode="HTML" | |
| ) | |
| await state.set_state(AdminStates.waiting_broadcast_message) | |
| await callback.answer() | |
| 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"β <b>Broadcast yakunlandi!</b>\n\n" | |
| f"π€ Yuborildi: <b>{success_count}</b> ta\n" | |
| f"β Xato: <b>{fail_count}</b> ta\n" | |
| f"π Jami: <b>{total}</b> ta", | |
| parse_mode="HTML", | |
| reply_markup=admin_back_kb() | |
| ) | |
| # βββββββββββββββββββ NARX O'ZGARTIRISH βββββββββββββββββββ | |
| 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( | |
| "π° <b>Narxni O'zgartirish</b>\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() | |
| 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"π° <b>{product['name']}</b>\n\n" | |
| f"Joriy narx: <b>{product['stars_price']} β Stars</b>\n\n" | |
| f"Yangi narxni (faqat raqam) yuboring:", | |
| reply_markup=cancel_admin_action_kb(), | |
| parse_mode="HTML" | |
| ) | |
| await callback.answer() | |
| 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: <code>750</code>", | |
| 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"β <b>Narx muvaffaqiyatli o'zgartirildi!</b>\n\n" | |
| f"Yangi narx: <b>{new_price} β Stars</b>", | |
| parse_mode="HTML", | |
| reply_markup=admin_back_kb() | |
| ) | |