preiums_bot / handlers /admin.py
ibrohm's picture
Upload folder using huggingface_hub
d923177 verified
Raw
History Blame Contribute Delete
16.9 kB
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(
"πŸ› οΈ <b>Admin Panel</b>\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(
"πŸ› οΈ <b>Admin Panel</b>\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 = ["πŸ“¦ <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 ───────────────────
@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(
"βž• <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()
@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(
"πŸ”— <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()
@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 <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 ───────────────────
@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 = (
"πŸ“Š <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 ───────────────────
@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(
"βœ… <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()
@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"πŸŽ‰ <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 ───────────────────
@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"πŸ“’ <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()
@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"βœ… <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 ───────────────────
@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(
"πŸ’° <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()
@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"πŸ’° <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()
@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: <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()
)