Spaces:
Runtime error
Runtime error
| import os | |
| import logging | |
| from datetime import datetime | |
| from motor.motor_asyncio import AsyncIOMotorClient | |
| from config import MONGO_URI, PRODUCTS | |
| logger = logging.getLogger(__name__) | |
| # MongoDB Client | |
| client = AsyncIOMotorClient(MONGO_URI) | |
| db = client.get_database("telegram_bot") | |
| async def init_db(): | |
| """MongoDB ulanishini tekshirish va dastlabki sozlashlarni bajarish.""" | |
| try: | |
| # Indexlarni yaratish | |
| await db.users.create_index("id", unique=True) | |
| await db.products.create_index("id", unique=True) | |
| await db.gift_codes.create_index("id", unique=True) | |
| await db.gift_codes.create_index("gift_link", unique=True) | |
| await db.orders.create_index("id", unique=True) | |
| # Standart mahsulotlarni qo'shish (agar mavjud bo'lmasa) | |
| for p in PRODUCTS: | |
| await db.products.update_one( | |
| {"id": p["id"]}, | |
| {"$setOnInsert": { | |
| "id": p["id"], | |
| "name": p["name"], | |
| "description": p["description"], | |
| "duration_months": p["duration_months"], | |
| "stars_price": p["stars_price"], | |
| "emoji": p["emoji"], | |
| "is_active": 1 | |
| }}, | |
| upsert=True | |
| ) | |
| logger.info("β MongoDB indekslari va boshlang'ich mahsulotlar tayyor.") | |
| except Exception as e: | |
| logger.error(f"β MongoDB init_db xatosi: {e}", exc_info=True) | |
| async def get_next_sequence_value(sequence_name: str) -> int: | |
| """Auto-increment integer ID yaratish (SQLite o'rniga ishlatiladi).""" | |
| result = await db.counters.find_one_and_update( | |
| {"_id": sequence_name}, | |
| {"$inc": {"sequence_value": 1}}, | |
| upsert=True, | |
| return_document=True | |
| ) | |
| return result["sequence_value"] | |
| # βββββββββββββββββββββββ USERS βββββββββββββββββββββββ | |
| async def upsert_user(user_id: int, username: str | None, full_name: str): | |
| """Foydalanuvchini qo'shish yoki yangilash.""" | |
| await db.users.update_one( | |
| {"id": user_id}, | |
| {"$set": { | |
| "username": username, | |
| "full_name": full_name, | |
| "created_at": datetime.now().isoformat() | |
| }}, | |
| upsert=True | |
| ) | |
| async def get_all_user_ids() -> list[int]: | |
| """Barcha foydalanuvchilar ID larini olish (broadcast uchun).""" | |
| cursor = db.users.find({}, {"id": 1}) | |
| rows = await cursor.to_list(length=None) | |
| return [row["id"] for row in rows] | |
| async def count_users() -> int: | |
| """Foydalanuvchilar sonini hisoblash.""" | |
| return await db.users.count_documents({}) | |
| # βββββββββββββββββββββββ PRODUCTS βββββββββββββββββββββββ | |
| async def get_active_products() -> list[dict]: | |
| """Faol mahsulotlar ro'yxatini olish.""" | |
| cursor = db.products.find({"is_active": 1}).sort("duration_months", 1) | |
| rows = await cursor.to_list(length=None) | |
| return rows | |
| async def get_product_by_id(product_id: int) -> dict | None: | |
| """ID bo'yicha mahsulotni olish.""" | |
| return await db.products.find_one({"id": product_id}) | |
| async def update_product_price(product_id: int, new_price: int): | |
| """Mahsulot narxini yangilash.""" | |
| await db.products.update_one( | |
| {"id": product_id}, | |
| {"$set": {"stars_price": new_price}} | |
| ) | |
| # βββββββββββββββββββββββ GIFT CODES βββββββββββββββββββββββ | |
| async def add_gift_code(product_id: int, gift_link: str) -> bool: | |
| """Yangi sovg'a linkini zaxiraga qo'shish. Agar mavjud bo'lsa False qaytaradi.""" | |
| try: | |
| # O'xshash link borligini tekshiramiz (unique constraint) | |
| exists = await db.gift_codes.find_one({"gift_link": gift_link}) | |
| if exists: | |
| return False | |
| new_id = await get_next_sequence_value("gift_code_id") | |
| await db.gift_codes.insert_one({ | |
| "id": new_id, | |
| "product_id": product_id, | |
| "gift_link": gift_link, | |
| "is_used": 0, | |
| "used_by": None, | |
| "order_id": None, | |
| "added_at": datetime.now().isoformat(), | |
| "used_at": None | |
| }) | |
| return True | |
| except Exception: | |
| return False | |
| async def get_available_code(product_id: int) -> dict | None: | |
| """Berilgan mahsulot uchun birinchi ishlatilmagan linkni olish.""" | |
| return await db.gift_codes.find_one({ | |
| "product_id": product_id, | |
| "is_used": 0 | |
| }, sort=[("id", 1)]) | |
| async def mark_code_used(code_id: int, user_id: int, order_id: int): | |
| """Linkni ishlatilgan deb belgilash.""" | |
| await db.gift_codes.update_one( | |
| {"id": code_id}, | |
| {"$set": { | |
| "is_used": 1, | |
| "used_by": user_id, | |
| "order_id": order_id, | |
| "used_at": datetime.now().isoformat() | |
| }} | |
| ) | |
| async def count_available_codes(product_id: int) -> int: | |
| """Mahsulot uchun mavjud linklarni sanash.""" | |
| return await db.gift_codes.count_documents({ | |
| "product_id": product_id, | |
| "is_used": 0 | |
| }) | |
| async def get_stock_summary() -> list[dict]: | |
| """Har bir mahsulot uchun zaxira holati.""" | |
| products = await get_active_products() | |
| summary = [] | |
| for p in products: | |
| available = await db.gift_codes.count_documents({"product_id": p["id"], "is_used": 0}) | |
| used = await db.gift_codes.count_documents({"product_id": p["id"], "is_used": 1}) | |
| total = available + used | |
| summary.append({ | |
| "name": p["name"], | |
| "emoji": p.get("emoji", "β"), | |
| "stars_price": p["stars_price"], | |
| "available": available, | |
| "used": used, | |
| "total": total | |
| }) | |
| return summary | |
| # βββββββββββββββββββββββ ORDERS βββββββββββββββββββββββ | |
| async def create_order(user_id: int, product_id: int, stars_amount: int) -> int: | |
| """Yangi buyurtma yaratish va uning ID sini qaytarish.""" | |
| new_id = await get_next_sequence_value("order_id") | |
| await db.orders.insert_one({ | |
| "id": new_id, | |
| "user_id": user_id, | |
| "product_id": product_id, | |
| "gift_code_id": None, | |
| "status": "pending", | |
| "charge_id": None, | |
| "stars_amount": stars_amount, | |
| "created_at": datetime.now().isoformat(), | |
| "completed_at": None | |
| }) | |
| return new_id | |
| async def complete_order(order_id: int, gift_code_id: int, charge_id: str): | |
| """Buyurtmani yakunlash.""" | |
| await db.orders.update_one( | |
| {"id": order_id}, | |
| {"$set": { | |
| "status": "completed", | |
| "gift_code_id": gift_code_id, | |
| "charge_id": charge_id, | |
| "completed_at": datetime.now().isoformat() | |
| }} | |
| ) | |
| async def fail_order(order_id: int, charge_id: str): | |
| """Buyurtmani (zaxira yo'qligi sababli) pending_delivery holatiga o'tkazish.""" | |
| await db.orders.update_one( | |
| {"id": order_id}, | |
| {"$set": { | |
| "status": "pending_delivery", | |
| "charge_id": charge_id | |
| }} | |
| ) | |
| async def get_user_orders(user_id: int) -> list[dict]: | |
| """Foydalanuvchi buyurtmalari tarixi (oxirgi 10 ta).""" | |
| cursor = db.orders.find({"user_id": user_id}).sort("created_at", -1).limit(10) | |
| orders = await cursor.to_list(length=None) | |
| result = [] | |
| for o in orders: | |
| product = await db.products.find_one({"id": o["product_id"]}) | |
| gift_link = None | |
| if o.get("gift_code_id"): | |
| code = await db.gift_codes.find_one({"id": o["gift_code_id"]}) | |
| if code: | |
| gift_link = code["gift_link"] | |
| result.append({ | |
| "id": o["id"], | |
| "status": o["status"], | |
| "stars_amount": o.get("stars_amount"), | |
| "created_at": o["created_at"], | |
| "product_name": product["name"] if product else "Noma'lum", | |
| "gift_link": gift_link | |
| }) | |
| return result | |
| async def get_total_stats() -> dict: | |
| """Umumiy statistika.""" | |
| total_completed = await db.orders.count_documents({"status": "completed"}) | |
| # Stars summasini hisoblash | |
| pipeline = [ | |
| {"$match": {"status": "completed"}}, | |
| {"$group": {"_id": None, "total": {"$sum": "$stars_amount"}}} | |
| ] | |
| cursor = db.orders.aggregate(pipeline) | |
| agg_result = await cursor.to_list(length=1) | |
| total_stars = agg_result[0]["total"] if agg_result else 0 | |
| pending_delivery = await db.orders.count_documents({"status": "pending_delivery"}) | |
| return { | |
| "total_completed": total_completed, | |
| "total_stars": total_stars, | |
| "pending_delivery": pending_delivery | |
| } | |
| async def get_pending_delivery_orders() -> list[dict]: | |
| """Zaxira bo'lmaganligi sababli kutilayotgan buyurtmalar.""" | |
| cursor = db.orders.find({"status": "pending_delivery"}).sort("created_at", 1) | |
| orders = await cursor.to_list(length=None) | |
| result = [] | |
| for o in orders: | |
| product = await db.products.find_one({"id": o["product_id"]}) | |
| user = await db.users.find_one({"id": o["user_id"]}) | |
| result.append({ | |
| "id": o["id"], | |
| "user_id": o["user_id"], | |
| "stars_amount": o.get("stars_amount"), | |
| "created_at": o["created_at"], | |
| "product_name": product["name"] if product else "Noma'lum", | |
| "product_id": o["product_id"], | |
| "username": user.get("username") if user else None, | |
| "full_name": user.get("full_name") if user else "Noma'lum" | |
| }) | |
| return result | |