""" MTXtyle - Kiyim va Aksessuarlar Do'koni Telegram Bot """ import logging import logging import openpyxl import shutil import os from io import BytesIO from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove from telegram.ext import ( Application, CommandHandler, CallbackQueryHandler, ConversationHandler, MessageHandler, filters ) from config import * from database import * # Logging configuration logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("bot.log"), logging.StreamHandler() ] ) logger = logging.getLogger(__name__) # ==================== MIDDLEWARE (Rate Limiting) ==================== import time from collections import defaultdict class ThrottlingMiddleware: def __init__(self, limit=1.0): self.rate_limit = limit self.last_time = defaultdict(float) async def __call__(self, update: Update, context): if not update.effective_user: return user_id = update.effective_user.id current_time = time.time() if current_time - self.last_time[user_id] < self.rate_limit: # Too fast return self.last_time[user_id] = current_time # ConversationHandler states (ADD_NAME, ADD_DESC, ADD_PRICE, ADD_CATEGORY, ADD_PHOTO) = range(5) (ORDER_NAME, ORDER_PHONE, ORDER_ADDRESS) = range(10, 13) (EDIT_FIELD, EDIT_VALUE) = range(20, 22) (BROADCAST_MSG,) = range(40, 41) SEARCH_QUERY = 30 # ==================== HELPERS ==================== def is_admin(user_id): return user_id in ADMIN_IDS def format_price(price): return f"{price:,} {CURRENCY}".replace(",", " ") def main_menu_keyboard(): return InlineKeyboardMarkup([ [InlineKeyboardButton("šŸ› Katalog", callback_data="catalog"), InlineKeyboardButton("šŸ” Qidirish", callback_data="search")], [InlineKeyboardButton("šŸ›’ Savat", callback_data="cart"), InlineKeyboardButton("šŸ“¦ Buyurtmalarim", callback_data="my_orders")], [InlineKeyboardButton("šŸ“ž Bog'lanish", callback_data="contact"), InlineKeyboardButton("ā„¹ļø Yordam", callback_data="help")], ]) # ==================== START & MENU ==================== async def start_command(update: Update, context): user = update.effective_user await add_user(user.id, user.username, user.first_name) if ADMIN_IDS == []: # Birinchi foydalanuvchini admin qilib belgilash ADMIN_IDS.append(user.id) logger.info(f"Admin belgilandi: {user.id}") text = ( f"Assalomu alaykum, {user.first_name}! šŸ‘‹\n\n" f"šŸŖ **{STORE_NAME}** ga xush kelibsiz!\n\n" f"{STORE_DESCRIPTION}\n\n" f"Quyidagi tugmalardan birini tanlang:" ) if update.callback_query: await update.callback_query.edit_message_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown") else: await update.message.reply_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown") # ==================== CATALOG ==================== async def show_categories(update: Update, context): query = update.callback_query await query.answer() categories = await get_categories() buttons = [] for cat in categories: count_text = "" prods, total = await get_products_by_category(cat["id"], 0, 1) count_text = f" ({total})" buttons.append([InlineKeyboardButton(f"{cat['emoji']} {cat['name']}{count_text}", callback_data=f"cat_{cat['id']}")]) buttons.append([InlineKeyboardButton("šŸ”™ Orqaga", callback_data="main_menu")]) await query.edit_message_text("šŸ“‚ **Kategoriyalarni tanlang:**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def show_products(update: Update, context): query = update.callback_query await query.answer() data = query.data parts = data.split("_") category_id = int(parts[1]) page = int(parts[2]) if len(parts) > 2 else 0 products, total = await get_products_by_category(category_id, page, PRODUCTS_PER_PAGE) category = await get_category_by_id(category_id) if not products: buttons = [[InlineKeyboardButton("šŸ”™ Kategoriyalar", callback_data="catalog")]] await query.edit_message_text(f"šŸ˜” Bu kategoriyada hozircha mahsulot yo'q.", reply_markup=InlineKeyboardMarkup(buttons)) return text = f"{category['emoji']} **{category['name']}**\n\n" buttons = [] for p in products: text += f"šŸ“Œ *{p['name']}*\nšŸ’° {format_price(p['price'])}\n\n" buttons.append([ InlineKeyboardButton(f"šŸ‘ {p['name']}", callback_data=f"prod_{p['id']}"), InlineKeyboardButton("šŸ›’ Qo'shish", callback_data=f"addcart_{p['id']}") ]) # Pagination nav_buttons = [] total_pages = (total + PRODUCTS_PER_PAGE - 1) // PRODUCTS_PER_PAGE if page > 0: nav_buttons.append(InlineKeyboardButton("ā¬…ļø", callback_data=f"cat_{category_id}_{page-1}")) nav_buttons.append(InlineKeyboardButton(f"{page+1}/{total_pages}", callback_data="noop")) if page < total_pages - 1: nav_buttons.append(InlineKeyboardButton("āž”ļø", callback_data=f"cat_{category_id}_{page+1}")) if nav_buttons: buttons.append(nav_buttons) buttons.append([InlineKeyboardButton("šŸ”™ Kategoriyalar", callback_data="catalog")]) await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def show_product_detail(update: Update, context): query = update.callback_query await query.answer() product_id = int(query.data.split("_")[1]) product = await get_product_by_id(product_id) if not product: await query.edit_message_text("āŒ Mahsulot topilmadi.") return text = ( f"šŸ“Œ **{product['name']}**\n\n" f"šŸ“ {product['description']}\n\n" f"šŸ’° Narxi: **{format_price(product['price'])}**\n" f"{'āœ… Mavjud' if product['in_stock'] else 'āŒ Tugagan'}" ) buttons = [ [InlineKeyboardButton("šŸ›’ Savatga qo'shish", callback_data=f"addcart_{product_id}")], [InlineKeyboardButton("šŸ”™ Orqaga", callback_data=f"cat_{product['category_id']}")] ] if product.get("photo_id"): await query.message.delete() await query.message.chat.send_photo( photo=product["photo_id"], caption=text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown" ) else: await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # ==================== CART ==================== async def add_to_cart_handler(update: Update, context): query = update.callback_query product_id = int(query.data.split("_")[1]) user_id = query.from_user.id await add_to_cart(user_id, product_id, 1) await query.answer("āœ… Savatga qo'shildi!", show_alert=True) async def show_cart(update: Update, context): query = update.callback_query await query.answer() user_id = query.from_user.id cart = await get_cart(user_id) if not cart: buttons = [[InlineKeyboardButton("šŸ› Katalog", callback_data="catalog"), InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]] await query.edit_message_text("šŸ›’ Savatingiz bo'sh.", reply_markup=InlineKeyboardMarkup(buttons)) return total = await get_cart_total(user_id) text = "šŸ›’ **Savatingiz:**\n\n" buttons = [] for item in cart: subtotal = item["price"] * item["quantity"] text += f"šŸ“Œ {item['name']}\n {item['quantity']} x {format_price(item['price'])} = {format_price(subtotal)}\n\n" buttons.append([ InlineKeyboardButton("āž–", callback_data=f"cartminus_{item['product_id']}"), InlineKeyboardButton(f"{item['quantity']} dona", callback_data="noop"), InlineKeyboardButton("āž•", callback_data=f"cartplus_{item['product_id']}"), InlineKeyboardButton("šŸ—‘", callback_data=f"cartdel_{item['product_id']}") ]) text += f"šŸ’° **Jami: {format_price(total)}**" buttons.append([InlineKeyboardButton("āœ… Buyurtma berish", callback_data="checkout")]) buttons.append([InlineKeyboardButton("šŸ—‘ Savatni tozalash", callback_data="clear_cart"), InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]) await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def cart_plus(update: Update, context): query = update.callback_query product_id = int(query.data.split("_")[1]) await add_to_cart(query.from_user.id, product_id, 1) await show_cart(update, context) async def cart_minus(update: Update, context): query = update.callback_query product_id = int(query.data.split("_")[1]) await add_to_cart(query.from_user.id, product_id, -1) await show_cart(update, context) async def cart_delete(update: Update, context): query = update.callback_query product_id = int(query.data.split("_")[1]) await remove_from_cart(query.from_user.id, product_id) await show_cart(update, context) async def clear_cart_handler(update: Update, context): query = update.callback_query await clear_cart(query.from_user.id) await query.answer("šŸ—‘ Savat tozalandi!", show_alert=True) await show_cart(update, context) # ==================== CHECKOUT ==================== async def checkout_start(update: Update, context): query = update.callback_query await query.answer() cart = await get_cart(query.from_user.id) if not cart: await query.edit_message_text("šŸ›’ Savatingiz bo'sh!") return ConversationHandler.END keyboard = [[InlineKeyboardButton("šŸ”™ Bekor qilis", callback_data="cancel_checkout")]] await query.edit_message_text("šŸ“ **Buyurtma berish**\n\nIsmingizni kiriting:", parse_mode="Markdown", reply_markup=InlineKeyboardMarkup(keyboard)) return ORDER_NAME async def checkout_name(update: Update, context): if update.callback_query and update.callback_query.data == "cancel_checkout": await checkout_cancel(update, context) return ConversationHandler.END context.user_data["order_name"] = update.message.text contact_btn = KeyboardButton("šŸ“± Raqamni yuborish", request_contact=True) reply_markup = ReplyKeyboardMarkup([[contact_btn]], resize_keyboard=True, one_time_keyboard=True) await update.message.reply_text( "šŸ“± Telefon raqamingizni kiriting:\n(Masalan: +998901234567)\nYoki pastdagi tugmani bosing:", reply_markup=reply_markup ) return ORDER_PHONE async def checkout_phone(update: Update, context): if update.message.contact: phone = update.message.contact.phone_number else: phone = update.message.text # Simple validation import re if not re.match(r"^\+?[0-9]{9,15}$", phone): await update.message.reply_text("āŒ Noto'g'ri format! Qaytadan kiriting yoki tugmani bosing:") return ORDER_PHONE context.user_data["order_phone"] = phone await update.message.reply_text("šŸ“ Yetkazib berish manzilini kiriting:", reply_markup=ReplyKeyboardRemove()) return ORDER_ADDRESS async def checkout_address(update: Update, context): user_id = update.effective_user.id full_name = context.user_data.get("order_name", "") phone = context.user_data.get("order_phone", "") address = update.message.text order_id = await create_order(user_id, full_name, phone, address) if not order_id: await update.message.reply_text("āŒ Xatolik! Savatingiz bo'sh.", reply_markup=main_menu_keyboard()) return ConversationHandler.END order = await get_order(order_id) items = await get_order_items(order_id) text = ( f"āœ… **Buyurtma #{order_id} qabul qilindi!**\n\n" f"šŸ‘¤ {full_name}\nšŸ“± {phone}\nšŸ“ {address}\n\n" f"šŸ“¦ Mahsulotlar:\n" ) for item in items: text += f" • {item['product_name']} x{item['quantity']} — {format_price(item['price'] * item['quantity'])}\n" text += f"\nšŸ’° **Jami: {format_price(order['total_price'])}**\n\n" text += "šŸ“ž Tez orada siz bilan bog'lanamiz!" await update.message.reply_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown") # Adminga xabar for admin_id in ADMIN_IDS: try: admin_text = ( f"šŸ†• **Yangi buyurtma #{order_id}!**\n\n" f"šŸ‘¤ {full_name}\nšŸ“± {phone}\nšŸ“ {address}\n" f"šŸ’° Jami: {format_price(order['total_price'])}\n\n" ) for item in items: admin_text += f" • {item['product_name']} x{item['quantity']}\n" admin_buttons = [[ InlineKeyboardButton("āœ… Qabul qilish", callback_data=f"orderstatus_{order_id}_qabul_qilindi"), InlineKeyboardButton("āŒ Bekor", callback_data=f"orderstatus_{order_id}_bekor_qilindi") ]] await context.bot.send_message(admin_id, admin_text, reply_markup=InlineKeyboardMarkup(admin_buttons), parse_mode="Markdown") except Exception as e: logger.error(f"Admin xabar xato: {e}") return ConversationHandler.END async def checkout_cancel(update: Update, context): # Remove keyboard if present reply_markup = ReplyKeyboardRemove() if update.callback_query: await update.callback_query.message.reply_text("āŒ Buyurtma bekor qilindi.", reply_markup=reply_markup) await update.callback_query.message.reply_text("šŸ  Asosiy menyu:", reply_markup=main_menu_keyboard()) else: await update.message.reply_text("āŒ Buyurtma bekor qilindi.", reply_markup=reply_markup) await update.message.reply_text("šŸ  Asosiy menyu:", reply_markup=main_menu_keyboard()) return ConversationHandler.END # ==================== MY ORDERS ==================== async def show_my_orders(update: Update, context): query = update.callback_query await query.answer() orders = await get_user_orders(query.from_user.id) if not orders: buttons = [[InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]] await query.edit_message_text("šŸ“¦ Sizda hali buyurtmalar yo'q.", reply_markup=InlineKeyboardMarkup(buttons)) return text = "šŸ“¦ **Buyurtmalaringiz:**\n\n" for o in orders[:10]: status = ORDER_STATUSES.get(o["status"], o["status"]) text += f"šŸ”¹ #{o['id']} — {format_price(o['total_price'])} — {status}\n" buttons = [[InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]] await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # ==================== SEARCH ==================== async def search_start(update: Update, context): query = update.callback_query await query.answer() await query.edit_message_text("šŸ” Mahsulot nomini yozing:") return SEARCH_QUERY async def search_results(update: Update, context): query_text = update.message.text products = await search_products(query_text) if not products: await update.message.reply_text( f"šŸ˜” \"{query_text}\" bo'yicha hech narsa topilmadi.", reply_markup=main_menu_keyboard() ) return ConversationHandler.END text = f"šŸ” **Natijalar: \"{query_text}\"**\n\n" buttons = [] for p in products[:10]: text += f"šŸ“Œ {p['name']} — {format_price(p['price'])}\n" buttons.append([ InlineKeyboardButton(f"šŸ‘ {p['name']}", callback_data=f"prod_{p['id']}"), InlineKeyboardButton("šŸ›’", callback_data=f"addcart_{p['id']}") ]) buttons.append([InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]) await update.message.reply_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") return ConversationHandler.END # ==================== CONTACT & HELP ==================== async def show_contact(update: Update, context): query = update.callback_query await query.answer() text = f"šŸ“ž **Bog'lanish**\n\n{CONTACT_INFO}\n\nSavollaringiz bo'lsa, yozing!" buttons = [[InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]] await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def show_help(update: Update, context): query = update.callback_query await query.answer() text = ( "ā„¹ļø **Qanday foydalanish:**\n\n" "1ļøāƒ£ šŸ› Katalogdan mahsulot tanlang\n" "2ļøāƒ£ šŸ›’ Savatga qo'shing\n" "3ļøāƒ£ āœ… Buyurtma bering\n" "4ļøāƒ£ šŸ“ž Biz siz bilan bog'lanamiz\n\n" "šŸ” Qidirish orqali kerakli mahsulotni topishingiz mumkin!" ) buttons = [[InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")]] await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # ==================== ADMIN ==================== async def admin_command(update: Update, context): if not is_admin(update.effective_user.id): await update.message.reply_text("ā›” Sizda admin huquqi yo'q!") return buttons = [ [InlineKeyboardButton("āž• Mahsulot qo'shish", callback_data="admin_add"), InlineKeyboardButton("šŸ“‹ Mahsulotlar", callback_data="admin_products")], [InlineKeyboardButton("šŸ“¦ Buyurtmalar", callback_data="admin_orders"), InlineKeyboardButton("šŸ“Š Statistika", callback_data="admin_stats")], [InlineKeyboardButton("šŸ“¢ Broadcast", callback_data="admin_broadcast"), InlineKeyboardButton("šŸ“‚ Export Excel", callback_data="admin_export")], [InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")] ] await update.message.reply_text("āš™ļø **Admin Panel**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def admin_panel_callback(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›” Admin emas!", show_alert=True) return await query.answer() buttons = [ [InlineKeyboardButton("āž• Mahsulot qo'shish", callback_data="admin_add"), InlineKeyboardButton("šŸ“‹ Mahsulotlar", callback_data="admin_products")], [InlineKeyboardButton("šŸ“¦ Buyurtmalar", callback_data="admin_orders"), InlineKeyboardButton("šŸ“Š Statistika", callback_data="admin_stats")], [InlineKeyboardButton("šŸ“¢ Broadcast", callback_data="admin_broadcast"), InlineKeyboardButton("šŸ“‚ Export Excel", callback_data="admin_export")], [InlineKeyboardButton("šŸ”™ Menyu", callback_data="main_menu")] ] await query.edit_message_text("āš™ļø **Admin Panel**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # Admin: Mahsulot qo'shish async def admin_add_start(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return ConversationHandler.END await query.answer() await query.edit_message_text("šŸ“ Mahsulot nomini kiriting:") return ADD_NAME async def admin_add_name(update: Update, context): context.user_data["new_product"] = {"name": update.message.text} await update.message.reply_text("šŸ“ Tavsifini kiriting:") return ADD_DESC async def admin_add_desc(update: Update, context): context.user_data["new_product"]["description"] = update.message.text await update.message.reply_text("šŸ’° Narxini kiriting (faqat raqam, so'mda):") return ADD_PRICE async def admin_add_price(update: Update, context): try: price = int(update.message.text.replace(" ", "").replace(",", "")) context.user_data["new_product"]["price"] = price except ValueError: await update.message.reply_text("āŒ Noto'g'ri raqam! Qaytadan kiriting:") return ADD_PRICE categories = await get_categories() buttons = [] for cat in categories: buttons.append([InlineKeyboardButton(f"{cat['emoji']} {cat['name']}", callback_data=f"newcat_{cat['id']}")]) await update.message.reply_text("šŸ“‚ Kategoriyani tanlang:", reply_markup=InlineKeyboardMarkup(buttons)) return ADD_CATEGORY async def admin_add_category(update: Update, context): query = update.callback_query await query.answer() category_id = int(query.data.split("_")[1]) context.user_data["new_product"]["category_id"] = category_id await query.edit_message_text("šŸ“· Mahsulot rasmini yuboring (yoki /skip bosing):") return ADD_PHOTO async def admin_add_photo(update: Update, context): if update.message.photo: photo_id = update.message.photo[-1].file_id context.user_data["new_product"]["photo_id"] = photo_id elif update.message.text and update.message.text == "/skip": context.user_data["new_product"]["photo_id"] = "" else: await update.message.reply_text("šŸ“· Rasm yuboring yoki /skip bosing:") return ADD_PHOTO p = context.user_data["new_product"] product_id = await add_product(p["name"], p["description"], p["price"], p["category_id"], p.get("photo_id", "")) await update.message.reply_text( f"āœ… Mahsulot qo'shildi!\n\n" f"šŸ“Œ {p['name']}\nšŸ’° {format_price(p['price'])}\nšŸ†” ID: {product_id}", reply_markup=main_menu_keyboard() ) return ConversationHandler.END async def admin_add_cancel(update: Update, context): await update.message.reply_text("āŒ Bekor qilindi.", reply_markup=main_menu_keyboard()) return ConversationHandler.END # Admin: Mahsulotlar ro'yxati async def admin_show_products(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return await query.answer() products = await get_all_products() if not products: buttons = [[InlineKeyboardButton("šŸ”™ Admin", callback_data="admin_panel")]] await query.edit_message_text("šŸ“‹ Mahsulotlar yo'q.", reply_markup=InlineKeyboardMarkup(buttons)) return text = "šŸ“‹ **Mahsulotlar:**\n\n" buttons = [] for p in products[:20]: stock = "āœ…" if p["in_stock"] else "āŒ" text += f"{stock} #{p['id']} {p['name']} — {format_price(p['price'])}\n" buttons.append([ InlineKeyboardButton(f"āœļø #{p['id']}", callback_data=f"aedit_{p['id']}"), InlineKeyboardButton(f"šŸ—‘ #{p['id']}", callback_data=f"adel_{p['id']}") ]) buttons.append([InlineKeyboardButton("šŸ”™ Admin", callback_data="admin_panel")]) await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # Admin: Mahsulotni o'chirish async def admin_delete_product(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return product_id = int(query.data.split("_")[1]) product = await get_product_by_id(product_id) if product: await delete_product(product_id) await query.answer(f"šŸ—‘ '{product['name']}' o'chirildi!", show_alert=True) await admin_show_products(update, context) # Admin: Mahsulotni tahrirlash async def admin_edit_product(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return ConversationHandler.END await query.answer() product_id = int(query.data.split("_")[1]) product = await get_product_by_id(product_id) if not product: await query.edit_message_text("āŒ Mahsulot topilmadi.") return ConversationHandler.END context.user_data["edit_product_id"] = product_id buttons = [ [InlineKeyboardButton("šŸ“Œ Nom", callback_data="editf_name"), InlineKeyboardButton("šŸ’° Narx", callback_data="editf_price")], [InlineKeyboardButton("šŸ“ Tavsif", callback_data="editf_description"), InlineKeyboardButton("šŸ“· Rasm", callback_data="editf_photo")], [InlineKeyboardButton("šŸ”„ Stock", callback_data=f"togglestock_{product_id}")], [InlineKeyboardButton("šŸ”™ Orqaga", callback_data="admin_products")] ] text = f"āœļø **Tahrirlash: {product['name']}**\n\nNimani o'zgartirmoqchisiz?" await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") return EDIT_FIELD async def admin_edit_field(update: Update, context): query = update.callback_query await query.answer() field = query.data.split("_")[1] context.user_data["edit_field"] = field labels = {"name": "Yangi nom", "price": "Yangi narx (raqam)", "description": "Yangi tavsif"} await query.edit_message_text(f"āœļø {labels.get(field, field)} kiriting:") return EDIT_VALUE async def admin_edit_value(update: Update, context): field = context.user_data.get("edit_field") product_id = context.user_data.get("edit_product_id") value = update.message.text if field == "price": try: value = int(value.replace(" ", "").replace(",", "")) except ValueError: await update.message.reply_text("āŒ Noto'g'ri raqam!") return EDIT_VALUE if field == "photo": if update.message.photo: value = update.message.photo[-1].file_id field = "photo_id" else: await update.message.reply_text("šŸ“· Rasm yuboring!") return EDIT_VALUE await update_product(product_id, **{field: value}) await update.message.reply_text(f"āœ… Yangilandi!", reply_markup=main_menu_keyboard()) return ConversationHandler.END # Admin: Stock toggle async def admin_toggle_stock(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return product_id = int(query.data.split("_")[1]) product = await get_product_by_id(product_id) if product: new_stock = 0 if product["in_stock"] else 1 await update_product(product_id, in_stock=new_stock) status = "āœ… Mavjud" if new_stock else "āŒ Tugagan" await query.answer(f"Status: {status}", show_alert=True) # Admin: Buyurtmalar async def admin_show_orders(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return await query.answer() orders = await get_all_orders() if not orders: buttons = [[InlineKeyboardButton("šŸ”™ Admin", callback_data="admin_panel")]] await query.edit_message_text("šŸ“¦ Buyurtmalar yo'q.", reply_markup=InlineKeyboardMarkup(buttons)) return text = "šŸ“¦ **Buyurtmalar:**\n\n" buttons = [] for o in orders[:15]: status = ORDER_STATUSES.get(o["status"], o["status"]) text += f"#{o['id']} | {o['full_name']} | {format_price(o['total_price'])} | {status}\n" buttons.append([InlineKeyboardButton(f"šŸ‘ #{o['id']}", callback_data=f"orderdetail_{o['id']}")]) buttons.append([InlineKeyboardButton("šŸ”™ Admin", callback_data="admin_panel")]) await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def admin_order_detail(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return await query.answer() order_id = int(query.data.split("_")[1]) order = await get_order(order_id) items = await get_order_items(order_id) if not order: await query.edit_message_text("āŒ Buyurtma topilmadi.") return status = ORDER_STATUSES.get(order["status"], order["status"]) text = ( f"šŸ“¦ **Buyurtma #{order_id}**\n\n" f"šŸ‘¤ {order['full_name']}\nšŸ“± {order['phone']}\nšŸ“ {order['address']}\n" f"šŸ“Š Status: {status}\nšŸ’° Jami: {format_price(order['total_price'])}\n\n" f"šŸ“‹ Mahsulotlar:\n" ) for item in items: text += f" • {item['product_name']} x{item['quantity']} — {format_price(item['price'] * item['quantity'])}\n" buttons = [] for s_key, s_val in ORDER_STATUSES.items(): if s_key != order["status"]: buttons.append([InlineKeyboardButton(s_val, callback_data=f"orderstatus_{order_id}_{s_key}")]) buttons.append([InlineKeyboardButton("šŸ”™ Buyurtmalar", callback_data="admin_orders")]) await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") async def admin_change_order_status(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return parts = query.data.split("_") order_id = int(parts[1]) new_status = "_".join(parts[2:]) await update_order_status(order_id, new_status) status_text = ORDER_STATUSES.get(new_status, new_status) await query.answer(f"Status: {status_text}", show_alert=True) # Mijozga xabar order = await get_order(order_id) if order: try: await context.bot.send_message( order["user_id"], f"šŸ“¦ Buyurtma #{order_id} statusi: {status_text}" ) except Exception: pass await admin_order_detail(update, context) # Admin: Statistika async def admin_show_stats(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return await query.answer() stats = await get_stats() text = ( f"šŸ“Š **Statistika**\n\n" f"šŸ“¦ Mahsulotlar: {stats['products_count']}\n" f"šŸ›’ Buyurtmalar: {stats['orders_count']}\n" f"šŸ†• Yangi buyurtmalar: {stats['new_orders']}\n" f"šŸ‘„ Mijozlar: {stats['unique_customers']}\n" f"šŸ’° Umumiy daromad: {format_price(stats['total_revenue'])}" ) buttons = [[InlineKeyboardButton("šŸ”™ Admin", callback_data="admin_panel")]] await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown") # Admin: Export Excel async def admin_export(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return await query.answer("ā³ Fayl tayyorlanmoqda...") try: orders = await get_all_orders_with_items() wb = openpyxl.Workbook() ws = wb.active ws.title = "Buyurtmalar" headers = ["ID", "Sana", "Mijoz", "Telefon", "Manzil", "Mahsulot", "Soni", "Narx", "Status", "Jami"] ws.append(headers) for o in orders: row = [ o["id"], time.strftime('%Y-%m-%d %H:%M', time.localtime(o["created_at"])), o["full_name"], o["phone"], o["address"], o["product_name"], o["quantity"], o["price"], o["status"], o["total_price"] ] ws.append(row) bio = BytesIO() wb.save(bio) bio.seek(0) await query.message.reply_document( document=bio, filename=f"orders_{time.strftime('%Y%m%d')}.xlsx", caption="šŸ“‚ Buyurtmalar tarixi" ) except Exception as e: logger.error(f"Export error: {e}") await query.message.reply_text("āŒ Xatolik yuz berdi.") # Admin: Broadcast async def admin_broadcast_start(update: Update, context): query = update.callback_query if not is_admin(query.from_user.id): await query.answer("ā›”", show_alert=True) return ConversationHandler.END await query.answer() await query.message.reply_text("šŸ“¢ Xabarni yuboring (matn, rasm yoki forward):") return BROADCAST_MSG async def admin_broadcast_send(update: Update, context): user_id = update.effective_user.id users = await get_all_users() count = 0 status_msg = await update.message.reply_text(f"ā³ Yuborilmoqda... (0/{len(users)})") for uid in users: try: await update.message.copy(uid) count += 1 except Exception: pass if count % 10 == 0: try: await status_msg.edit_text(f"ā³ Yuborilmoqda... ({count}/{len(users)})") except Exception: pass await status_msg.edit_text(f"āœ… Xabar {count} ta foydalanuvchiga yuborildi.", reply_markup=main_menu_keyboard()) return ConversationHandler.END async def admin_broadcast_cancel(update: Update, context): await update.message.reply_text("āŒ Bekor qilindi.", reply_markup=main_menu_keyboard()) return ConversationHandler.END # Admin: Backup async def admin_backup(update: Update, context): if not is_admin(update.effective_user.id): return try: if not os.path.exists("backups"): os.makedirs("backups") backup_path = f"backups/store_backup_{int(time.time())}.db" shutil.copy(DATABASE_PATH, backup_path) await update.message.reply_document( document=open(backup_path, "rb"), caption=f"āœ… Baza nusxalandi: {backup_path}" ) except Exception as e: logger.error(f"Backup error: {e}") await update.message.reply_text("āŒ Backup xatolik.") # ==================== NOOP ==================== async def noop_callback(update: Update, context): await update.callback_query.answer() # ==================== MAIN ==================== def main(): app = Application.builder().token(BOT_TOKEN).build() # Checkout conversation checkout_conv = ConversationHandler( entry_points=[CallbackQueryHandler(checkout_start, pattern="^checkout$")], states={ ORDER_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_name)], ORDER_PHONE: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_phone)], ORDER_ADDRESS: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_address)], }, fallbacks=[CommandHandler("cancel", checkout_cancel)], ) # Admin add product conversation admin_add_conv = ConversationHandler( entry_points=[CallbackQueryHandler(admin_add_start, pattern="^admin_add$")], states={ ADD_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_name)], ADD_DESC: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_desc)], ADD_PRICE: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_price)], ADD_CATEGORY: [CallbackQueryHandler(admin_add_category, pattern="^newcat_")], ADD_PHOTO: [ MessageHandler(filters.PHOTO, admin_add_photo), CommandHandler("skip", admin_add_photo), ], }, fallbacks=[CommandHandler("cancel", admin_add_cancel)], ) # Admin edit product conversation admin_edit_conv = ConversationHandler( entry_points=[CallbackQueryHandler(admin_edit_product, pattern=r"^aedit_\d+$")], states={ EDIT_FIELD: [ CallbackQueryHandler(admin_edit_field, pattern="^editf_"), CallbackQueryHandler(admin_toggle_stock, pattern="^togglestock_"), CallbackQueryHandler(admin_show_products, pattern="^admin_products$"), ], EDIT_VALUE: [ MessageHandler(filters.TEXT & ~filters.COMMAND, admin_edit_value), MessageHandler(filters.PHOTO, admin_edit_value), ], }, fallbacks=[CommandHandler("cancel", admin_add_cancel)], ) # Search conversation search_conv = ConversationHandler( entry_points=[CallbackQueryHandler(search_start, pattern="^search$")], states={ SEARCH_QUERY: [MessageHandler(filters.TEXT & ~filters.COMMAND, search_results)], }, fallbacks=[CommandHandler("cancel", checkout_cancel)], ) # Admin broadcast conversation admin_broadcast_conv = ConversationHandler( entry_points=[CallbackQueryHandler(admin_broadcast_start, pattern="^admin_broadcast$")], states={ BROADCAST_MSG: [ MessageHandler(filters.ALL & ~filters.COMMAND, admin_broadcast_send) ], }, fallbacks=[CommandHandler("cancel", admin_broadcast_cancel)], ) # Add conversations first app.add_handler(checkout_conv) app.add_handler(admin_add_conv) app.add_handler(admin_edit_conv) app.add_handler(admin_broadcast_conv) app.add_handler(search_conv) # Commands app.add_handler(CommandHandler("start", start_command)) app.add_handler(CommandHandler("admin", admin_command)) app.add_handler(CommandHandler("backup", admin_backup)) # Callback handlers app.add_handler(CallbackQueryHandler(start_command, pattern="^main_menu$")) app.add_handler(CallbackQueryHandler(show_categories, pattern="^catalog$")) app.add_handler(CallbackQueryHandler(show_products, pattern=r"^cat_\d+")) app.add_handler(CallbackQueryHandler(show_product_detail, pattern=r"^prod_\d+$")) app.add_handler(CallbackQueryHandler(add_to_cart_handler, pattern=r"^addcart_\d+$")) app.add_handler(CallbackQueryHandler(show_cart, pattern="^cart$")) app.add_handler(CallbackQueryHandler(cart_plus, pattern=r"^cartplus_\d+$")) app.add_handler(CallbackQueryHandler(cart_minus, pattern=r"^cartminus_\d+$")) app.add_handler(CallbackQueryHandler(cart_delete, pattern=r"^cartdel_\d+$")) app.add_handler(CallbackQueryHandler(clear_cart_handler, pattern="^clear_cart$")) app.add_handler(CallbackQueryHandler(show_my_orders, pattern="^my_orders$")) app.add_handler(CallbackQueryHandler(show_contact, pattern="^contact$")) app.add_handler(CallbackQueryHandler(show_help, pattern="^help$")) app.add_handler(CallbackQueryHandler(admin_panel_callback, pattern="^admin_panel$")) app.add_handler(CallbackQueryHandler(admin_show_products, pattern="^admin_products$")) app.add_handler(CallbackQueryHandler(admin_delete_product, pattern=r"^adel_\d+$")) app.add_handler(CallbackQueryHandler(admin_show_orders, pattern="^admin_orders$")) app.add_handler(CallbackQueryHandler(admin_order_detail, pattern=r"^orderdetail_\d+$")) app.add_handler(CallbackQueryHandler(admin_change_order_status, pattern=r"^orderstatus_")) app.add_handler(CallbackQueryHandler(admin_show_stats, pattern="^admin_stats$")) app.add_handler(CallbackQueryHandler(admin_export, pattern="^admin_export$")) app.add_handler(CallbackQueryHandler(noop_callback, pattern="^noop$")) # Init DB on startup import asyncio asyncio.get_event_loop().run_until_complete(init_db()) logger.info(f"šŸ¤– {STORE_NAME} bot ishga tushdi!") app.run_polling(drop_pending_updates=True) if __name__ == "__main__": main()