Deploy Bot commited on
Commit
c9f1a26
·
0 Parent(s):

Initial commit of V1.0 - Features Complete

Browse files
Files changed (6) hide show
  1. .gitignore +16 -0
  2. bot.py +961 -0
  3. config.py +48 -0
  4. database.py +473 -0
  5. requirements.txt +0 -0
  6. verify_db.py +44 -0
.gitignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .env
6
+ .venv
7
+ env/
8
+ venv/
9
+ ENV/
10
+ env.bak/
11
+ venv.bak/
12
+ store.db
13
+ bot.log
14
+ backups/
15
+ .idea/
16
+ .vscode/
bot.py ADDED
@@ -0,0 +1,961 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MTXtyle - Kiyim va Aksessuarlar Do'koni Telegram Bot
3
+ """
4
+ import logging
5
+ import logging
6
+ import openpyxl
7
+ import shutil
8
+ import os
9
+ from io import BytesIO
10
+ from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
11
+ from telegram.ext import (
12
+ Application, CommandHandler, CallbackQueryHandler,
13
+ ConversationHandler, MessageHandler, filters
14
+ )
15
+ from config import *
16
+ from database import *
17
+
18
+ # Logging configuration
19
+ logging.basicConfig(
20
+ level=logging.INFO,
21
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
22
+ handlers=[
23
+ logging.FileHandler("bot.log"),
24
+ logging.StreamHandler()
25
+ ]
26
+ )
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # ==================== MIDDLEWARE (Rate Limiting) ====================
30
+ import time
31
+ from collections import defaultdict
32
+
33
+ class ThrottlingMiddleware:
34
+ def __init__(self, limit=1.0):
35
+ self.rate_limit = limit
36
+ self.last_time = defaultdict(float)
37
+
38
+ async def __call__(self, update: Update, context):
39
+ if not update.effective_user:
40
+ return
41
+
42
+ user_id = update.effective_user.id
43
+ current_time = time.time()
44
+
45
+ if current_time - self.last_time[user_id] < self.rate_limit:
46
+ # Too fast
47
+ return
48
+
49
+ self.last_time[user_id] = current_time
50
+
51
+ # ConversationHandler states
52
+ (ADD_NAME, ADD_DESC, ADD_PRICE, ADD_CATEGORY, ADD_PHOTO) = range(5)
53
+ (ORDER_NAME, ORDER_PHONE, ORDER_ADDRESS) = range(10, 13)
54
+ (EDIT_FIELD, EDIT_VALUE) = range(20, 22)
55
+ (BROADCAST_MSG,) = range(40, 41)
56
+ SEARCH_QUERY = 30
57
+
58
+ # ==================== HELPERS ====================
59
+
60
+ def is_admin(user_id):
61
+ return user_id in ADMIN_IDS
62
+
63
+ def format_price(price):
64
+ return f"{price:,} {CURRENCY}".replace(",", " ")
65
+
66
+ def main_menu_keyboard():
67
+ return InlineKeyboardMarkup([
68
+ [InlineKeyboardButton("🛍 Katalog", callback_data="catalog"),
69
+ InlineKeyboardButton("🔍 Qidirish", callback_data="search")],
70
+ [InlineKeyboardButton("🛒 Savat", callback_data="cart"),
71
+ InlineKeyboardButton("📦 Buyurtmalarim", callback_data="my_orders")],
72
+ [InlineKeyboardButton("📞 Bog'lanish", callback_data="contact"),
73
+ InlineKeyboardButton("ℹ️ Yordam", callback_data="help")],
74
+ ])
75
+
76
+ # ==================== START & MENU ====================
77
+
78
+ async def start_command(update: Update, context):
79
+ user = update.effective_user
80
+ await add_user(user.id, user.username, user.first_name)
81
+
82
+ if ADMIN_IDS == []:
83
+ # Birinchi foydalanuvchini admin qilib belgilash
84
+ ADMIN_IDS.append(user.id)
85
+ logger.info(f"Admin belgilandi: {user.id}")
86
+
87
+ text = (
88
+ f"Assalomu alaykum, {user.first_name}! 👋\n\n"
89
+ f"🏪 **{STORE_NAME}** ga xush kelibsiz!\n\n"
90
+ f"{STORE_DESCRIPTION}\n\n"
91
+ f"Quyidagi tugmalardan birini tanlang:"
92
+ )
93
+
94
+ if update.callback_query:
95
+ await update.callback_query.edit_message_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown")
96
+ else:
97
+ await update.message.reply_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown")
98
+
99
+ # ==================== CATALOG ====================
100
+
101
+ async def show_categories(update: Update, context):
102
+ query = update.callback_query
103
+ await query.answer()
104
+ categories = await get_categories()
105
+
106
+ buttons = []
107
+ for cat in categories:
108
+ count_text = ""
109
+ prods, total = await get_products_by_category(cat["id"], 0, 1)
110
+ count_text = f" ({total})"
111
+ buttons.append([InlineKeyboardButton(f"{cat['emoji']} {cat['name']}{count_text}", callback_data=f"cat_{cat['id']}")])
112
+
113
+ buttons.append([InlineKeyboardButton("🔙 Orqaga", callback_data="main_menu")])
114
+ await query.edit_message_text("📂 **Kategoriyalarni tanlang:**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
115
+
116
+ async def show_products(update: Update, context):
117
+ query = update.callback_query
118
+ await query.answer()
119
+ data = query.data
120
+
121
+ parts = data.split("_")
122
+ category_id = int(parts[1])
123
+ page = int(parts[2]) if len(parts) > 2 else 0
124
+
125
+ products, total = await get_products_by_category(category_id, page, PRODUCTS_PER_PAGE)
126
+ category = await get_category_by_id(category_id)
127
+
128
+ if not products:
129
+ buttons = [[InlineKeyboardButton("🔙 Kategoriyalar", callback_data="catalog")]]
130
+ await query.edit_message_text(f"😔 Bu kategoriyada hozircha mahsulot yo'q.", reply_markup=InlineKeyboardMarkup(buttons))
131
+ return
132
+
133
+ text = f"{category['emoji']} **{category['name']}**\n\n"
134
+ buttons = []
135
+
136
+ for p in products:
137
+ text += f"📌 *{p['name']}*\n💰 {format_price(p['price'])}\n\n"
138
+ buttons.append([
139
+ InlineKeyboardButton(f"👁 {p['name']}", callback_data=f"prod_{p['id']}"),
140
+ InlineKeyboardButton("🛒 Qo'shish", callback_data=f"addcart_{p['id']}")
141
+ ])
142
+
143
+ # Pagination
144
+ nav_buttons = []
145
+ total_pages = (total + PRODUCTS_PER_PAGE - 1) // PRODUCTS_PER_PAGE
146
+ if page > 0:
147
+ nav_buttons.append(InlineKeyboardButton("⬅️", callback_data=f"cat_{category_id}_{page-1}"))
148
+ nav_buttons.append(InlineKeyboardButton(f"{page+1}/{total_pages}", callback_data="noop"))
149
+ if page < total_pages - 1:
150
+ nav_buttons.append(InlineKeyboardButton("➡️", callback_data=f"cat_{category_id}_{page+1}"))
151
+
152
+ if nav_buttons:
153
+ buttons.append(nav_buttons)
154
+ buttons.append([InlineKeyboardButton("🔙 Kategoriyalar", callback_data="catalog")])
155
+
156
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
157
+
158
+ async def show_product_detail(update: Update, context):
159
+ query = update.callback_query
160
+ await query.answer()
161
+ product_id = int(query.data.split("_")[1])
162
+ product = await get_product_by_id(product_id)
163
+
164
+ if not product:
165
+ await query.edit_message_text("❌ Mahsulot topilmadi.")
166
+ return
167
+
168
+ text = (
169
+ f"📌 **{product['name']}**\n\n"
170
+ f"📝 {product['description']}\n\n"
171
+ f"💰 Narxi: **{format_price(product['price'])}**\n"
172
+ f"{'✅ Mavjud' if product['in_stock'] else '❌ Tugagan'}"
173
+ )
174
+
175
+ buttons = [
176
+ [InlineKeyboardButton("🛒 Savatga qo'shish", callback_data=f"addcart_{product_id}")],
177
+ [InlineKeyboardButton("🔙 Orqaga", callback_data=f"cat_{product['category_id']}")]
178
+ ]
179
+
180
+ if product.get("photo_id"):
181
+ await query.message.delete()
182
+ await query.message.chat.send_photo(
183
+ photo=product["photo_id"], caption=text,
184
+ reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown"
185
+ )
186
+ else:
187
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
188
+
189
+ # ==================== CART ====================
190
+
191
+ async def add_to_cart_handler(update: Update, context):
192
+ query = update.callback_query
193
+ product_id = int(query.data.split("_")[1])
194
+ user_id = query.from_user.id
195
+ await add_to_cart(user_id, product_id, 1)
196
+ await query.answer("✅ Savatga qo'shildi!", show_alert=True)
197
+
198
+ async def show_cart(update: Update, context):
199
+ query = update.callback_query
200
+ await query.answer()
201
+ user_id = query.from_user.id
202
+ cart = await get_cart(user_id)
203
+
204
+ if not cart:
205
+ buttons = [[InlineKeyboardButton("🛍 Katalog", callback_data="catalog"),
206
+ InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]]
207
+ await query.edit_message_text("🛒 Savatingiz bo'sh.", reply_markup=InlineKeyboardMarkup(buttons))
208
+ return
209
+
210
+ total = await get_cart_total(user_id)
211
+ text = "🛒 **Savatingiz:**\n\n"
212
+ buttons = []
213
+
214
+ for item in cart:
215
+ subtotal = item["price"] * item["quantity"]
216
+ text += f"📌 {item['name']}\n {item['quantity']} x {format_price(item['price'])} = {format_price(subtotal)}\n\n"
217
+ buttons.append([
218
+ InlineKeyboardButton("➖", callback_data=f"cartminus_{item['product_id']}"),
219
+ InlineKeyboardButton(f"{item['quantity']} dona", callback_data="noop"),
220
+ InlineKeyboardButton("➕", callback_data=f"cartplus_{item['product_id']}"),
221
+ InlineKeyboardButton("🗑", callback_data=f"cartdel_{item['product_id']}")
222
+ ])
223
+
224
+ text += f"💰 **Jami: {format_price(total)}**"
225
+ buttons.append([InlineKeyboardButton("✅ Buyurtma berish", callback_data="checkout")])
226
+ buttons.append([InlineKeyboardButton("🗑 Savatni tozalash", callback_data="clear_cart"),
227
+ InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")])
228
+
229
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
230
+
231
+ async def cart_plus(update: Update, context):
232
+ query = update.callback_query
233
+ product_id = int(query.data.split("_")[1])
234
+ await add_to_cart(query.from_user.id, product_id, 1)
235
+ await show_cart(update, context)
236
+
237
+ async def cart_minus(update: Update, context):
238
+ query = update.callback_query
239
+ product_id = int(query.data.split("_")[1])
240
+ await add_to_cart(query.from_user.id, product_id, -1)
241
+ await show_cart(update, context)
242
+
243
+ async def cart_delete(update: Update, context):
244
+ query = update.callback_query
245
+ product_id = int(query.data.split("_")[1])
246
+ await remove_from_cart(query.from_user.id, product_id)
247
+ await show_cart(update, context)
248
+
249
+ async def clear_cart_handler(update: Update, context):
250
+ query = update.callback_query
251
+ await clear_cart(query.from_user.id)
252
+ await query.answer("🗑 Savat tozalandi!", show_alert=True)
253
+ await show_cart(update, context)
254
+
255
+ # ==================== CHECKOUT ====================
256
+
257
+ async def checkout_start(update: Update, context):
258
+ query = update.callback_query
259
+ await query.answer()
260
+ cart = await get_cart(query.from_user.id)
261
+ if not cart:
262
+ await query.edit_message_text("🛒 Savatingiz bo'sh!")
263
+ return ConversationHandler.END
264
+
265
+ keyboard = [[InlineKeyboardButton("🔙 Bekor qilis", callback_data="cancel_checkout")]]
266
+ await query.edit_message_text("📝 **Buyurtma berish**\n\nIsmingizni kiriting:", parse_mode="Markdown", reply_markup=InlineKeyboardMarkup(keyboard))
267
+ return ORDER_NAME
268
+
269
+ async def checkout_name(update: Update, context):
270
+ if update.callback_query and update.callback_query.data == "cancel_checkout":
271
+ await checkout_cancel(update, context)
272
+ return ConversationHandler.END
273
+
274
+ context.user_data["order_name"] = update.message.text
275
+
276
+ contact_btn = KeyboardButton("📱 Raqamni yuborish", request_contact=True)
277
+ reply_markup = ReplyKeyboardMarkup([[contact_btn]], resize_keyboard=True, one_time_keyboard=True)
278
+
279
+ await update.message.reply_text(
280
+ "📱 Telefon raqamingizni kiriting:\n(Masalan: +998901234567)\nYoki pastdagi tugmani bosing:",
281
+ reply_markup=reply_markup
282
+ )
283
+ return ORDER_PHONE
284
+
285
+ async def checkout_phone(update: Update, context):
286
+ if update.message.contact:
287
+ phone = update.message.contact.phone_number
288
+ else:
289
+ phone = update.message.text
290
+ # Simple validation
291
+ import re
292
+ if not re.match(r"^\+?[0-9]{9,15}$", phone):
293
+ await update.message.reply_text("❌ Noto'g'ri format! Qaytadan kiriting yoki tugmani bosing:")
294
+ return ORDER_PHONE
295
+
296
+ context.user_data["order_phone"] = phone
297
+ await update.message.reply_text("📍 Yetkazib berish manzilini kiriting:", reply_markup=ReplyKeyboardRemove())
298
+ return ORDER_ADDRESS
299
+
300
+ async def checkout_address(update: Update, context):
301
+ user_id = update.effective_user.id
302
+ full_name = context.user_data.get("order_name", "")
303
+ phone = context.user_data.get("order_phone", "")
304
+ address = update.message.text
305
+
306
+ order_id = await create_order(user_id, full_name, phone, address)
307
+ if not order_id:
308
+ await update.message.reply_text("❌ Xatolik! Savatingiz bo'sh.", reply_markup=main_menu_keyboard())
309
+ return ConversationHandler.END
310
+
311
+ order = await get_order(order_id)
312
+ items = await get_order_items(order_id)
313
+
314
+ text = (
315
+ f"✅ **Buyurtma #{order_id} qabul qilindi!**\n\n"
316
+ f"👤 {full_name}\n📱 {phone}\n📍 {address}\n\n"
317
+ f"📦 Mahsulotlar:\n"
318
+ )
319
+ for item in items:
320
+ text += f" • {item['product_name']} x{item['quantity']} — {format_price(item['price'] * item['quantity'])}\n"
321
+ text += f"\n💰 **Jami: {format_price(order['total_price'])}**\n\n"
322
+ text += "📞 Tez orada siz bilan bog'lanamiz!"
323
+
324
+ await update.message.reply_text(text, reply_markup=main_menu_keyboard(), parse_mode="Markdown")
325
+
326
+ # Adminga xabar
327
+ for admin_id in ADMIN_IDS:
328
+ try:
329
+ admin_text = (
330
+ f"🆕 **Yangi buyurtma #{order_id}!**\n\n"
331
+ f"👤 {full_name}\n📱 {phone}\n📍 {address}\n"
332
+ f"💰 Jami: {format_price(order['total_price'])}\n\n"
333
+ )
334
+ for item in items:
335
+ admin_text += f" • {item['product_name']} x{item['quantity']}\n"
336
+
337
+ admin_buttons = [[
338
+ InlineKeyboardButton("✅ Qabul qilish", callback_data=f"orderstatus_{order_id}_qabul_qilindi"),
339
+ InlineKeyboardButton("❌ Bekor", callback_data=f"orderstatus_{order_id}_bekor_qilindi")
340
+ ]]
341
+ await context.bot.send_message(admin_id, admin_text, reply_markup=InlineKeyboardMarkup(admin_buttons), parse_mode="Markdown")
342
+ except Exception as e:
343
+ logger.error(f"Admin xabar xato: {e}")
344
+
345
+ return ConversationHandler.END
346
+
347
+ async def checkout_cancel(update: Update, context):
348
+ # Remove keyboard if present
349
+ reply_markup = ReplyKeyboardRemove()
350
+ if update.callback_query:
351
+ await update.callback_query.message.reply_text("❌ Buyurtma bekor qilindi.", reply_markup=reply_markup)
352
+ await update.callback_query.message.reply_text("🏠 Asosiy menyu:", reply_markup=main_menu_keyboard())
353
+ else:
354
+ await update.message.reply_text("❌ Buyurtma bekor qilindi.", reply_markup=reply_markup)
355
+ await update.message.reply_text("🏠 Asosiy menyu:", reply_markup=main_menu_keyboard())
356
+ return ConversationHandler.END
357
+
358
+ # ==================== MY ORDERS ====================
359
+
360
+ async def show_my_orders(update: Update, context):
361
+ query = update.callback_query
362
+ await query.answer()
363
+ orders = await get_user_orders(query.from_user.id)
364
+
365
+ if not orders:
366
+ buttons = [[InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]]
367
+ await query.edit_message_text("📦 Sizda hali buyurtmalar yo'q.", reply_markup=InlineKeyboardMarkup(buttons))
368
+ return
369
+
370
+ text = "📦 **Buyurtmalaringiz:**\n\n"
371
+ for o in orders[:10]:
372
+ status = ORDER_STATUSES.get(o["status"], o["status"])
373
+ text += f"🔹 #{o['id']} — {format_price(o['total_price'])} — {status}\n"
374
+
375
+ buttons = [[InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]]
376
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
377
+
378
+ # ==================== SEARCH ====================
379
+
380
+ async def search_start(update: Update, context):
381
+ query = update.callback_query
382
+ await query.answer()
383
+ await query.edit_message_text("🔍 Mahsulot nomini yozing:")
384
+ return SEARCH_QUERY
385
+
386
+ async def search_results(update: Update, context):
387
+ query_text = update.message.text
388
+ products = await search_products(query_text)
389
+
390
+ if not products:
391
+ await update.message.reply_text(
392
+ f"😔 \"{query_text}\" bo'yicha hech narsa topilmadi.",
393
+ reply_markup=main_menu_keyboard()
394
+ )
395
+ return ConversationHandler.END
396
+
397
+ text = f"🔍 **Natijalar: \"{query_text}\"**\n\n"
398
+ buttons = []
399
+ for p in products[:10]:
400
+ text += f"📌 {p['name']} — {format_price(p['price'])}\n"
401
+ buttons.append([
402
+ InlineKeyboardButton(f"👁 {p['name']}", callback_data=f"prod_{p['id']}"),
403
+ InlineKeyboardButton("🛒", callback_data=f"addcart_{p['id']}")
404
+ ])
405
+ buttons.append([InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")])
406
+ await update.message.reply_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
407
+ return ConversationHandler.END
408
+
409
+ # ==================== CONTACT & HELP ====================
410
+
411
+ async def show_contact(update: Update, context):
412
+ query = update.callback_query
413
+ await query.answer()
414
+ text = f"📞 **Bog'lanish**\n\n{CONTACT_INFO}\n\nSavollaringiz bo'lsa, yozing!"
415
+ buttons = [[InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]]
416
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
417
+
418
+ async def show_help(update: Update, context):
419
+ query = update.callback_query
420
+ await query.answer()
421
+ text = (
422
+ "ℹ️ **Qanday foydalanish:**\n\n"
423
+ "1️⃣ 🛍 Katalogdan mahsulot tanlang\n"
424
+ "2️⃣ 🛒 Savatga qo'shing\n"
425
+ "3️⃣ ✅ Buyurtma bering\n"
426
+ "4️⃣ 📞 Biz siz bilan bog'lanamiz\n\n"
427
+ "🔍 Qidirish orqali kerakli mahsulotni topishingiz mumkin!"
428
+ )
429
+ buttons = [[InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]]
430
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
431
+
432
+ # ==================== ADMIN ====================
433
+
434
+ async def admin_command(update: Update, context):
435
+ if not is_admin(update.effective_user.id):
436
+ await update.message.reply_text("⛔ Sizda admin huquqi yo'q!")
437
+ return
438
+ buttons = [
439
+ [InlineKeyboardButton("➕ Mahsulot qo'shish", callback_data="admin_add"),
440
+ InlineKeyboardButton("📋 Mahsulotlar", callback_data="admin_products")],
441
+ [InlineKeyboardButton("📦 Buyurtmalar", callback_data="admin_orders"),
442
+ InlineKeyboardButton("📊 Statistika", callback_data="admin_stats")],
443
+ [InlineKeyboardButton("📢 Broadcast", callback_data="admin_broadcast"),
444
+ InlineKeyboardButton("📂 Export Excel", callback_data="admin_export")],
445
+ [InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]
446
+ ]
447
+ await update.message.reply_text("⚙️ **Admin Panel**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
448
+
449
+ async def admin_panel_callback(update: Update, context):
450
+ query = update.callback_query
451
+ if not is_admin(query.from_user.id):
452
+ await query.answer("⛔ Admin emas!", show_alert=True)
453
+ return
454
+ await query.answer()
455
+ buttons = [
456
+ [InlineKeyboardButton("➕ Mahsulot qo'shish", callback_data="admin_add"),
457
+ InlineKeyboardButton("📋 Mahsulotlar", callback_data="admin_products")],
458
+ [InlineKeyboardButton("📦 Buyurtmalar", callback_data="admin_orders"),
459
+ InlineKeyboardButton("📊 Statistika", callback_data="admin_stats")],
460
+ [InlineKeyboardButton("📢 Broadcast", callback_data="admin_broadcast"),
461
+ InlineKeyboardButton("📂 Export Excel", callback_data="admin_export")],
462
+ [InlineKeyboardButton("🔙 Menyu", callback_data="main_menu")]
463
+ ]
464
+ await query.edit_message_text("⚙️ **Admin Panel**", reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
465
+
466
+ # Admin: Mahsulot qo'shish
467
+ async def admin_add_start(update: Update, context):
468
+ query = update.callback_query
469
+ if not is_admin(query.from_user.id):
470
+ await query.answer("⛔", show_alert=True)
471
+ return ConversationHandler.END
472
+ await query.answer()
473
+ await query.edit_message_text("📝 Mahsulot nomini kiriting:")
474
+ return ADD_NAME
475
+
476
+ async def admin_add_name(update: Update, context):
477
+ context.user_data["new_product"] = {"name": update.message.text}
478
+ await update.message.reply_text("📝 Tavsifini kiriting:")
479
+ return ADD_DESC
480
+
481
+ async def admin_add_desc(update: Update, context):
482
+ context.user_data["new_product"]["description"] = update.message.text
483
+ await update.message.reply_text("💰 Narxini kiriting (faqat raqam, so'mda):")
484
+ return ADD_PRICE
485
+
486
+ async def admin_add_price(update: Update, context):
487
+ try:
488
+ price = int(update.message.text.replace(" ", "").replace(",", ""))
489
+ context.user_data["new_product"]["price"] = price
490
+ except ValueError:
491
+ await update.message.reply_text("❌ Noto'g'ri raqam! Qaytadan kiriting:")
492
+ return ADD_PRICE
493
+
494
+ categories = await get_categories()
495
+ buttons = []
496
+ for cat in categories:
497
+ buttons.append([InlineKeyboardButton(f"{cat['emoji']} {cat['name']}", callback_data=f"newcat_{cat['id']}")])
498
+ await update.message.reply_text("📂 Kategoriyani tanlang:", reply_markup=InlineKeyboardMarkup(buttons))
499
+ return ADD_CATEGORY
500
+
501
+ async def admin_add_category(update: Update, context):
502
+ query = update.callback_query
503
+ await query.answer()
504
+ category_id = int(query.data.split("_")[1])
505
+ context.user_data["new_product"]["category_id"] = category_id
506
+ await query.edit_message_text("📷 Mahsulot rasmini yuboring (yoki /skip bosing):")
507
+ return ADD_PHOTO
508
+
509
+ async def admin_add_photo(update: Update, context):
510
+ if update.message.photo:
511
+ photo_id = update.message.photo[-1].file_id
512
+ context.user_data["new_product"]["photo_id"] = photo_id
513
+ elif update.message.text and update.message.text == "/skip":
514
+ context.user_data["new_product"]["photo_id"] = ""
515
+ else:
516
+ await update.message.reply_text("📷 Rasm yuboring yoki /skip bosing:")
517
+ return ADD_PHOTO
518
+
519
+ p = context.user_data["new_product"]
520
+ product_id = await add_product(p["name"], p["description"], p["price"], p["category_id"], p.get("photo_id", ""))
521
+
522
+ await update.message.reply_text(
523
+ f"✅ Mahsulot qo'shildi!\n\n"
524
+ f"📌 {p['name']}\n💰 {format_price(p['price'])}\n🆔 ID: {product_id}",
525
+ reply_markup=main_menu_keyboard()
526
+ )
527
+ return ConversationHandler.END
528
+
529
+ async def admin_add_cancel(update: Update, context):
530
+ await update.message.reply_text("❌ Bekor qilindi.", reply_markup=main_menu_keyboard())
531
+ return ConversationHandler.END
532
+
533
+ # Admin: Mahsulotlar ro'yxati
534
+ async def admin_show_products(update: Update, context):
535
+ query = update.callback_query
536
+ if not is_admin(query.from_user.id):
537
+ await query.answer("⛔", show_alert=True)
538
+ return
539
+ await query.answer()
540
+ products = await get_all_products()
541
+
542
+ if not products:
543
+ buttons = [[InlineKeyboardButton("🔙 Admin", callback_data="admin_panel")]]
544
+ await query.edit_message_text("📋 Mahsulotlar yo'q.", reply_markup=InlineKeyboardMarkup(buttons))
545
+ return
546
+
547
+ text = "📋 **Mahsulotlar:**\n\n"
548
+ buttons = []
549
+ for p in products[:20]:
550
+ stock = "✅" if p["in_stock"] else "❌"
551
+ text += f"{stock} #{p['id']} {p['name']} — {format_price(p['price'])}\n"
552
+ buttons.append([
553
+ InlineKeyboardButton(f"✏️ #{p['id']}", callback_data=f"aedit_{p['id']}"),
554
+ InlineKeyboardButton(f"🗑 #{p['id']}", callback_data=f"adel_{p['id']}")
555
+ ])
556
+ buttons.append([InlineKeyboardButton("🔙 Admin", callback_data="admin_panel")])
557
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
558
+
559
+ # Admin: Mahsulotni o'chirish
560
+ async def admin_delete_product(update: Update, context):
561
+ query = update.callback_query
562
+ if not is_admin(query.from_user.id):
563
+ await query.answer("⛔", show_alert=True)
564
+ return
565
+ product_id = int(query.data.split("_")[1])
566
+ product = await get_product_by_id(product_id)
567
+ if product:
568
+ await delete_product(product_id)
569
+ await query.answer(f"🗑 '{product['name']}' o'chirildi!", show_alert=True)
570
+ await admin_show_products(update, context)
571
+
572
+ # Admin: Mahsulotni tahrirlash
573
+ async def admin_edit_product(update: Update, context):
574
+ query = update.callback_query
575
+ if not is_admin(query.from_user.id):
576
+ await query.answer("⛔", show_alert=True)
577
+ return ConversationHandler.END
578
+ await query.answer()
579
+ product_id = int(query.data.split("_")[1])
580
+ product = await get_product_by_id(product_id)
581
+ if not product:
582
+ await query.edit_message_text("❌ Mahsulot topilmadi.")
583
+ return ConversationHandler.END
584
+
585
+ context.user_data["edit_product_id"] = product_id
586
+ buttons = [
587
+ [InlineKeyboardButton("📌 Nom", callback_data="editf_name"),
588
+ InlineKeyboardButton("💰 Narx", callback_data="editf_price")],
589
+ [InlineKeyboardButton("📝 Tavsif", callback_data="editf_description"),
590
+ InlineKeyboardButton("📷 Rasm", callback_data="editf_photo")],
591
+ [InlineKeyboardButton("🔄 Stock", callback_data=f"togglestock_{product_id}")],
592
+ [InlineKeyboardButton("🔙 Orqaga", callback_data="admin_products")]
593
+ ]
594
+ text = f"✏️ **Tahrirlash: {product['name']}**\n\nNimani o'zgartirmoqchisiz?"
595
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
596
+ return EDIT_FIELD
597
+
598
+ async def admin_edit_field(update: Update, context):
599
+ query = update.callback_query
600
+ await query.answer()
601
+ field = query.data.split("_")[1]
602
+ context.user_data["edit_field"] = field
603
+ labels = {"name": "Yangi nom", "price": "Yangi narx (raqam)", "description": "Yangi tavsif"}
604
+ await query.edit_message_text(f"✏️ {labels.get(field, field)} kiriting:")
605
+ return EDIT_VALUE
606
+
607
+ async def admin_edit_value(update: Update, context):
608
+ field = context.user_data.get("edit_field")
609
+ product_id = context.user_data.get("edit_product_id")
610
+ value = update.message.text
611
+
612
+ if field == "price":
613
+ try:
614
+ value = int(value.replace(" ", "").replace(",", ""))
615
+ except ValueError:
616
+ await update.message.reply_text("❌ Noto'g'ri raqam!")
617
+ return EDIT_VALUE
618
+
619
+ if field == "photo":
620
+ if update.message.photo:
621
+ value = update.message.photo[-1].file_id
622
+ field = "photo_id"
623
+ else:
624
+ await update.message.reply_text("📷 Rasm yuboring!")
625
+ return EDIT_VALUE
626
+
627
+ await update_product(product_id, **{field: value})
628
+ await update.message.reply_text(f"✅ Yangilandi!", reply_markup=main_menu_keyboard())
629
+ return ConversationHandler.END
630
+
631
+ # Admin: Stock toggle
632
+ async def admin_toggle_stock(update: Update, context):
633
+ query = update.callback_query
634
+ if not is_admin(query.from_user.id):
635
+ await query.answer("⛔", show_alert=True)
636
+ return
637
+ product_id = int(query.data.split("_")[1])
638
+ product = await get_product_by_id(product_id)
639
+ if product:
640
+ new_stock = 0 if product["in_stock"] else 1
641
+ await update_product(product_id, in_stock=new_stock)
642
+ status = "✅ Mavjud" if new_stock else "❌ Tugagan"
643
+ await query.answer(f"Status: {status}", show_alert=True)
644
+
645
+ # Admin: Buyurtmalar
646
+ async def admin_show_orders(update: Update, context):
647
+ query = update.callback_query
648
+ if not is_admin(query.from_user.id):
649
+ await query.answer("⛔", show_alert=True)
650
+ return
651
+ await query.answer()
652
+ orders = await get_all_orders()
653
+
654
+ if not orders:
655
+ buttons = [[InlineKeyboardButton("🔙 Admin", callback_data="admin_panel")]]
656
+ await query.edit_message_text("📦 Buyurtmalar yo'q.", reply_markup=InlineKeyboardMarkup(buttons))
657
+ return
658
+
659
+ text = "📦 **Buyurtmalar:**\n\n"
660
+ buttons = []
661
+ for o in orders[:15]:
662
+ status = ORDER_STATUSES.get(o["status"], o["status"])
663
+ text += f"#{o['id']} | {o['full_name']} | {format_price(o['total_price'])} | {status}\n"
664
+ buttons.append([InlineKeyboardButton(f"👁 #{o['id']}", callback_data=f"orderdetail_{o['id']}")])
665
+ buttons.append([InlineKeyboardButton("🔙 Admin", callback_data="admin_panel")])
666
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
667
+
668
+ async def admin_order_detail(update: Update, context):
669
+ query = update.callback_query
670
+ if not is_admin(query.from_user.id):
671
+ await query.answer("⛔", show_alert=True)
672
+ return
673
+ await query.answer()
674
+ order_id = int(query.data.split("_")[1])
675
+ order = await get_order(order_id)
676
+ items = await get_order_items(order_id)
677
+
678
+ if not order:
679
+ await query.edit_message_text("❌ Buyurtma topilmadi.")
680
+ return
681
+
682
+ status = ORDER_STATUSES.get(order["status"], order["status"])
683
+ text = (
684
+ f"📦 **Buyurtma #{order_id}**\n\n"
685
+ f"👤 {order['full_name']}\n📱 {order['phone']}\n📍 {order['address']}\n"
686
+ f"📊 Status: {status}\n💰 Jami: {format_price(order['total_price'])}\n\n"
687
+ f"📋 Mahsulotlar:\n"
688
+ )
689
+ for item in items:
690
+ text += f" • {item['product_name']} x{item['quantity']} — {format_price(item['price'] * item['quantity'])}\n"
691
+
692
+ buttons = []
693
+ for s_key, s_val in ORDER_STATUSES.items():
694
+ if s_key != order["status"]:
695
+ buttons.append([InlineKeyboardButton(s_val, callback_data=f"orderstatus_{order_id}_{s_key}")])
696
+ buttons.append([InlineKeyboardButton("🔙 Buyurtmalar", callback_data="admin_orders")])
697
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
698
+
699
+ async def admin_change_order_status(update: Update, context):
700
+ query = update.callback_query
701
+ if not is_admin(query.from_user.id):
702
+ await query.answer("⛔", show_alert=True)
703
+ return
704
+ parts = query.data.split("_")
705
+ order_id = int(parts[1])
706
+ new_status = "_".join(parts[2:])
707
+
708
+ await update_order_status(order_id, new_status)
709
+ status_text = ORDER_STATUSES.get(new_status, new_status)
710
+ await query.answer(f"Status: {status_text}", show_alert=True)
711
+
712
+ # Mijozga xabar
713
+ order = await get_order(order_id)
714
+ if order:
715
+ try:
716
+ await context.bot.send_message(
717
+ order["user_id"],
718
+ f"📦 Buyurtma #{order_id} statusi: {status_text}"
719
+ )
720
+ except Exception:
721
+ pass
722
+
723
+ await admin_order_detail(update, context)
724
+
725
+ # Admin: Statistika
726
+ async def admin_show_stats(update: Update, context):
727
+ query = update.callback_query
728
+ if not is_admin(query.from_user.id):
729
+ await query.answer("⛔", show_alert=True)
730
+ return
731
+ await query.answer()
732
+ stats = await get_stats()
733
+
734
+ text = (
735
+ f"📊 **Statistika**\n\n"
736
+ f"📦 Mahsulotlar: {stats['products_count']}\n"
737
+ f"🛒 Buyurtmalar: {stats['orders_count']}\n"
738
+ f"🆕 Yangi buyurtmalar: {stats['new_orders']}\n"
739
+ f"👥 Mijozlar: {stats['unique_customers']}\n"
740
+ f"💰 Umumiy daromad: {format_price(stats['total_revenue'])}"
741
+ )
742
+ buttons = [[InlineKeyboardButton("🔙 Admin", callback_data="admin_panel")]]
743
+ await query.edit_message_text(text, reply_markup=InlineKeyboardMarkup(buttons), parse_mode="Markdown")
744
+
745
+ # Admin: Export Excel
746
+ async def admin_export(update: Update, context):
747
+ query = update.callback_query
748
+ if not is_admin(query.from_user.id):
749
+ await query.answer("⛔", show_alert=True)
750
+ return
751
+
752
+ await query.answer("⏳ Fayl tayyorlanmoqda...")
753
+
754
+ try:
755
+ orders = await get_all_orders_with_items()
756
+
757
+ wb = openpyxl.Workbook()
758
+ ws = wb.active
759
+ ws.title = "Buyurtmalar"
760
+
761
+ headers = ["ID", "Sana", "Mijoz", "Telefon", "Manzil", "Mahsulot", "Soni", "Narx", "Status", "Jami"]
762
+ ws.append(headers)
763
+
764
+ for o in orders:
765
+ row = [
766
+ o["id"], time.strftime('%Y-%m-%d %H:%M', time.localtime(o["created_at"])),
767
+ o["full_name"], o["phone"], o["address"],
768
+ o["product_name"], o["quantity"], o["price"],
769
+ o["status"], o["total_price"]
770
+ ]
771
+ ws.append(row)
772
+
773
+ bio = BytesIO()
774
+ wb.save(bio)
775
+ bio.seek(0)
776
+
777
+ await query.message.reply_document(
778
+ document=bio,
779
+ filename=f"orders_{time.strftime('%Y%m%d')}.xlsx",
780
+ caption="📂 Buyurtmalar tarixi"
781
+ )
782
+ except Exception as e:
783
+ logger.error(f"Export error: {e}")
784
+ await query.message.reply_text("❌ Xatolik yuz berdi.")
785
+
786
+ # Admin: Broadcast
787
+ async def admin_broadcast_start(update: Update, context):
788
+ query = update.callback_query
789
+ if not is_admin(query.from_user.id):
790
+ await query.answer("⛔", show_alert=True)
791
+ return ConversationHandler.END
792
+ await query.answer()
793
+ await query.message.reply_text("📢 Xabarni yuboring (matn, rasm yoki forward):")
794
+ return BROADCAST_MSG
795
+
796
+ async def admin_broadcast_send(update: Update, context):
797
+ user_id = update.effective_user.id
798
+ users = await get_all_users()
799
+ count = 0
800
+
801
+ status_msg = await update.message.reply_text(f"⏳ Yuborilmoqda... (0/{len(users)})")
802
+
803
+ for uid in users:
804
+ try:
805
+ await update.message.copy(uid)
806
+ count += 1
807
+ except Exception:
808
+ pass
809
+
810
+ if count % 10 == 0:
811
+ try:
812
+ await status_msg.edit_text(f"⏳ Yuborilmoqda... ({count}/{len(users)})")
813
+ except Exception:
814
+ pass
815
+
816
+ await status_msg.edit_text(f"✅ Xabar {count} ta foydalanuvchiga yuborildi.", reply_markup=main_menu_keyboard())
817
+ return ConversationHandler.END
818
+
819
+ async def admin_broadcast_cancel(update: Update, context):
820
+ await update.message.reply_text("❌ Bekor qilindi.", reply_markup=main_menu_keyboard())
821
+ return ConversationHandler.END
822
+
823
+ # Admin: Backup
824
+ async def admin_backup(update: Update, context):
825
+ if not is_admin(update.effective_user.id):
826
+ return
827
+
828
+ try:
829
+ if not os.path.exists("backups"):
830
+ os.makedirs("backups")
831
+
832
+ backup_path = f"backups/store_backup_{int(time.time())}.db"
833
+ shutil.copy(DATABASE_PATH, backup_path)
834
+
835
+ await update.message.reply_document(
836
+ document=open(backup_path, "rb"),
837
+ caption=f"✅ Baza nusxalandi: {backup_path}"
838
+ )
839
+ except Exception as e:
840
+ logger.error(f"Backup error: {e}")
841
+ await update.message.reply_text("❌ Backup xatolik.")
842
+
843
+ # ==================== NOOP ====================
844
+
845
+ async def noop_callback(update: Update, context):
846
+ await update.callback_query.answer()
847
+
848
+ # ==================== MAIN ====================
849
+
850
+ def main():
851
+ app = Application.builder().token(BOT_TOKEN).build()
852
+
853
+ # Checkout conversation
854
+ checkout_conv = ConversationHandler(
855
+ entry_points=[CallbackQueryHandler(checkout_start, pattern="^checkout$")],
856
+ states={
857
+ ORDER_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_name)],
858
+ ORDER_PHONE: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_phone)],
859
+ ORDER_ADDRESS: [MessageHandler(filters.TEXT & ~filters.COMMAND, checkout_address)],
860
+ },
861
+ fallbacks=[CommandHandler("cancel", checkout_cancel)],
862
+ )
863
+
864
+ # Admin add product conversation
865
+ admin_add_conv = ConversationHandler(
866
+ entry_points=[CallbackQueryHandler(admin_add_start, pattern="^admin_add$")],
867
+ states={
868
+ ADD_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_name)],
869
+ ADD_DESC: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_desc)],
870
+ ADD_PRICE: [MessageHandler(filters.TEXT & ~filters.COMMAND, admin_add_price)],
871
+ ADD_CATEGORY: [CallbackQueryHandler(admin_add_category, pattern="^newcat_")],
872
+ ADD_PHOTO: [
873
+ MessageHandler(filters.PHOTO, admin_add_photo),
874
+ CommandHandler("skip", admin_add_photo),
875
+ ],
876
+ },
877
+ fallbacks=[CommandHandler("cancel", admin_add_cancel)],
878
+ )
879
+
880
+ # Admin edit product conversation
881
+ admin_edit_conv = ConversationHandler(
882
+ entry_points=[CallbackQueryHandler(admin_edit_product, pattern=r"^aedit_\d+$")],
883
+ states={
884
+ EDIT_FIELD: [
885
+ CallbackQueryHandler(admin_edit_field, pattern="^editf_"),
886
+ CallbackQueryHandler(admin_toggle_stock, pattern="^togglestock_"),
887
+ CallbackQueryHandler(admin_show_products, pattern="^admin_products$"),
888
+ ],
889
+ EDIT_VALUE: [
890
+ MessageHandler(filters.TEXT & ~filters.COMMAND, admin_edit_value),
891
+ MessageHandler(filters.PHOTO, admin_edit_value),
892
+ ],
893
+ },
894
+ fallbacks=[CommandHandler("cancel", admin_add_cancel)],
895
+ )
896
+
897
+ # Search conversation
898
+ search_conv = ConversationHandler(
899
+ entry_points=[CallbackQueryHandler(search_start, pattern="^search$")],
900
+ states={
901
+ SEARCH_QUERY: [MessageHandler(filters.TEXT & ~filters.COMMAND, search_results)],
902
+ },
903
+ fallbacks=[CommandHandler("cancel", checkout_cancel)],
904
+ )
905
+
906
+ # Admin broadcast conversation
907
+ admin_broadcast_conv = ConversationHandler(
908
+ entry_points=[CallbackQueryHandler(admin_broadcast_start, pattern="^admin_broadcast$")],
909
+ states={
910
+ BROADCAST_MSG: [
911
+ MessageHandler(filters.ALL & ~filters.COMMAND, admin_broadcast_send)
912
+ ],
913
+ },
914
+ fallbacks=[CommandHandler("cancel", admin_broadcast_cancel)],
915
+ )
916
+
917
+ # Add conversations first
918
+ app.add_handler(checkout_conv)
919
+ app.add_handler(admin_add_conv)
920
+ app.add_handler(admin_edit_conv)
921
+ app.add_handler(admin_broadcast_conv)
922
+ app.add_handler(search_conv)
923
+
924
+ # Commands
925
+ app.add_handler(CommandHandler("start", start_command))
926
+ app.add_handler(CommandHandler("admin", admin_command))
927
+ app.add_handler(CommandHandler("backup", admin_backup))
928
+
929
+ # Callback handlers
930
+ app.add_handler(CallbackQueryHandler(start_command, pattern="^main_menu$"))
931
+ app.add_handler(CallbackQueryHandler(show_categories, pattern="^catalog$"))
932
+ app.add_handler(CallbackQueryHandler(show_products, pattern=r"^cat_\d+"))
933
+ app.add_handler(CallbackQueryHandler(show_product_detail, pattern=r"^prod_\d+$"))
934
+ app.add_handler(CallbackQueryHandler(add_to_cart_handler, pattern=r"^addcart_\d+$"))
935
+ app.add_handler(CallbackQueryHandler(show_cart, pattern="^cart$"))
936
+ app.add_handler(CallbackQueryHandler(cart_plus, pattern=r"^cartplus_\d+$"))
937
+ app.add_handler(CallbackQueryHandler(cart_minus, pattern=r"^cartminus_\d+$"))
938
+ app.add_handler(CallbackQueryHandler(cart_delete, pattern=r"^cartdel_\d+$"))
939
+ app.add_handler(CallbackQueryHandler(clear_cart_handler, pattern="^clear_cart$"))
940
+ app.add_handler(CallbackQueryHandler(show_my_orders, pattern="^my_orders$"))
941
+ app.add_handler(CallbackQueryHandler(show_contact, pattern="^contact$"))
942
+ app.add_handler(CallbackQueryHandler(show_help, pattern="^help$"))
943
+ app.add_handler(CallbackQueryHandler(admin_panel_callback, pattern="^admin_panel$"))
944
+ app.add_handler(CallbackQueryHandler(admin_show_products, pattern="^admin_products$"))
945
+ app.add_handler(CallbackQueryHandler(admin_delete_product, pattern=r"^adel_\d+$"))
946
+ app.add_handler(CallbackQueryHandler(admin_show_orders, pattern="^admin_orders$"))
947
+ app.add_handler(CallbackQueryHandler(admin_order_detail, pattern=r"^orderdetail_\d+$"))
948
+ app.add_handler(CallbackQueryHandler(admin_change_order_status, pattern=r"^orderstatus_"))
949
+ app.add_handler(CallbackQueryHandler(admin_show_stats, pattern="^admin_stats$"))
950
+ app.add_handler(CallbackQueryHandler(admin_export, pattern="^admin_export$"))
951
+ app.add_handler(CallbackQueryHandler(noop_callback, pattern="^noop$"))
952
+
953
+ # Init DB on startup
954
+ import asyncio
955
+ asyncio.get_event_loop().run_until_complete(init_db())
956
+
957
+ logger.info(f"🤖 {STORE_NAME} bot ishga tushdi!")
958
+ app.run_polling(drop_pending_updates=True)
959
+
960
+ if __name__ == "__main__":
961
+ main()
config.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MTXtyle - Kiyim va Aksessuarlar Do'koni Bot Sozlamalari
3
+ """
4
+
5
+ import os
6
+ from dotenv import load_dotenv
7
+
8
+ load_dotenv()
9
+
10
+ # Bot Token
11
+ BOT_TOKEN = os.getenv("BOT_TOKEN")
12
+
13
+ # Admin Telegram ID (o'zingizning Telegram ID'ingizni kiriting)
14
+ # @userinfobot ga /start yuboring ID'ingizni bilish uchun
15
+ ADMIN_IDS = [6309900880] # Masalan: [123456789, 987654321]
16
+
17
+ # Database
18
+ DATABASE_PATH = "store.db"
19
+
20
+ # Do'kon sozlamalari
21
+ STORE_NAME = "MTXtyle"
22
+ STORE_DESCRIPTION = "🛍 Kiyim, kechak va aksessuarlar do'koni"
23
+ CURRENCY = "so'm"
24
+
25
+ # Kategoriyalar
26
+ CATEGORIES = {
27
+ "erkaklar": {"name": "👔 Erkaklar kiyimi", "emoji": "👔"},
28
+ "ayollar": {"name": "👗 Ayollar kiyimi", "emoji": "👗"},
29
+ "bolalar": {"name": "👶 Bolalar kiyimi", "emoji": "👶"},
30
+ "aksessuarlar": {"name": "💎 Aksessuarlar", "emoji": "💎"},
31
+ "oyoq_kiyim": {"name": "👟 Oyoq kiyimlar", "emoji": "👟"},
32
+ "sumkalar": {"name": "👜 Sumkalar", "emoji": "👜"},
33
+ }
34
+
35
+ # Buyurtma statuslari
36
+ ORDER_STATUSES = {
37
+ "yangi": "🆕 Yangi",
38
+ "qabul_qilindi": "✅ Qabul qilindi",
39
+ "yetkazilmoqda": "🚚 Yetkazilmoqda",
40
+ "yetkazildi": "📦 Yetkazildi",
41
+ "bekor_qilindi": "❌ Bekor qilindi",
42
+ }
43
+
44
+ # Pagination
45
+ PRODUCTS_PER_PAGE = 5
46
+
47
+ # Contact
48
+ CONTACT_INFO = "📞 Bog'lanish: @mtxtyle_admin"
database.py ADDED
@@ -0,0 +1,473 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ MTXtyle - Database operatsiyalari (SQLite + aiosqlite)
3
+ """
4
+ import aiosqlite
5
+ import time
6
+ from config import DATABASE_PATH, CATEGORIES
7
+
8
+
9
+ async def init_db():
10
+ """Jadvallarni yaratish"""
11
+ async with aiosqlite.connect(DATABASE_PATH) as db:
12
+ # Kategoriyalar jadvali
13
+ await db.execute("""
14
+ CREATE TABLE IF NOT EXISTS categories (
15
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
16
+ slug TEXT UNIQUE NOT NULL,
17
+ name TEXT NOT NULL,
18
+ emoji TEXT DEFAULT ''
19
+ )
20
+ """)
21
+
22
+ # Mahsulotlar jadvali
23
+ await db.execute("""
24
+ CREATE TABLE IF NOT EXISTS products (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ name TEXT NOT NULL,
27
+ description TEXT DEFAULT '',
28
+ price INTEGER NOT NULL,
29
+ category_id INTEGER,
30
+ photo_id TEXT DEFAULT '',
31
+ in_stock INTEGER DEFAULT 1,
32
+ created_at REAL DEFAULT 0,
33
+ FOREIGN KEY (category_id) REFERENCES categories(id)
34
+ )
35
+ """)
36
+
37
+ # Savat jadvali
38
+ await db.execute("""
39
+ CREATE TABLE IF NOT EXISTS cart_items (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ user_id INTEGER NOT NULL,
42
+ product_id INTEGER NOT NULL,
43
+ quantity INTEGER DEFAULT 1,
44
+ FOREIGN KEY (product_id) REFERENCES products(id)
45
+ )
46
+ """)
47
+
48
+ # Buyurtmalar jadvali
49
+ await db.execute("""
50
+ CREATE TABLE IF NOT EXISTS orders (
51
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
52
+ user_id INTEGER NOT NULL,
53
+ full_name TEXT NOT NULL,
54
+ phone TEXT NOT NULL,
55
+ address TEXT NOT NULL,
56
+ total_price INTEGER DEFAULT 0,
57
+ status TEXT DEFAULT 'yangi',
58
+ created_at REAL DEFAULT 0
59
+ )
60
+ """)
61
+
62
+ # Buyurtma elementlari
63
+ await db.execute("""
64
+ CREATE TABLE IF NOT EXISTS order_items (
65
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
66
+ order_id INTEGER NOT NULL,
67
+ product_id INTEGER NOT NULL,
68
+ product_name TEXT NOT NULL,
69
+ quantity INTEGER DEFAULT 1,
70
+ price INTEGER DEFAULT 0,
71
+ FOREIGN KEY (order_id) REFERENCES orders(id),
72
+ FOREIGN KEY (product_id) REFERENCES products(id)
73
+ )
74
+ """)
75
+
76
+ # Users table for broadcast
77
+ await db.execute("""
78
+ CREATE TABLE IF NOT EXISTS users (
79
+ user_id INTEGER PRIMARY KEY,
80
+ username TEXT,
81
+ full_name TEXT,
82
+ joined_at REAL
83
+ )
84
+ """)
85
+
86
+ await db.commit()
87
+
88
+ # Default kategoriyalarni qo'shish
89
+ for slug, info in CATEGORIES.items():
90
+ try:
91
+ await db.execute(
92
+ "INSERT OR IGNORE INTO categories (slug, name, emoji) VALUES (?, ?, ?)",
93
+ (slug, info["name"], info["emoji"])
94
+ )
95
+ except Exception:
96
+ pass
97
+ await db.commit()
98
+
99
+
100
+ # ==================== KATEGORIYALAR ====================
101
+
102
+ async def get_categories():
103
+ """Barcha kategoriyalarni olish"""
104
+ async with aiosqlite.connect(DATABASE_PATH) as db:
105
+ db.row_factory = aiosqlite.Row
106
+ cursor = await db.execute("SELECT * FROM categories ORDER BY id")
107
+ rows = await cursor.fetchall()
108
+ return [dict(row) for row in rows]
109
+
110
+
111
+ async def get_category_by_slug(slug):
112
+ """Slug bo'yicha kategoriya olish"""
113
+ async with aiosqlite.connect(DATABASE_PATH) as db:
114
+ db.row_factory = aiosqlite.Row
115
+ cursor = await db.execute("SELECT * FROM categories WHERE slug = ?", (slug,))
116
+ row = await cursor.fetchone()
117
+ return dict(row) if row else None
118
+
119
+
120
+ async def get_category_by_id(category_id):
121
+ """ID bo'yicha kategoriya olish"""
122
+ async with aiosqlite.connect(DATABASE_PATH) as db:
123
+ db.row_factory = aiosqlite.Row
124
+ cursor = await db.execute("SELECT * FROM categories WHERE id = ?", (category_id,))
125
+ row = await cursor.fetchone()
126
+ return dict(row) if row else None
127
+
128
+
129
+ # ==================== MAHSULOTLAR ====================
130
+
131
+ async def add_product(name, description, price, category_id, photo_id=""):
132
+ """Yangi mahsulot qo'shish"""
133
+ async with aiosqlite.connect(DATABASE_PATH) as db:
134
+ cursor = await db.execute(
135
+ """INSERT INTO products (name, description, price, category_id, photo_id, created_at)
136
+ VALUES (?, ?, ?, ?, ?, ?)""",
137
+ (name, description, price, category_id, photo_id, time.time())
138
+ )
139
+ await db.commit()
140
+ return cursor.lastrowid
141
+
142
+
143
+ async def get_products_by_category(category_id, page=0, per_page=5):
144
+ """Kategoriya bo'yicha mahsulotlarni olish (pagination bilan)"""
145
+ async with aiosqlite.connect(DATABASE_PATH) as db:
146
+ db.row_factory = aiosqlite.Row
147
+ offset = page * per_page
148
+
149
+ # Umumiy sonni olish
150
+ cursor = await db.execute(
151
+ "SELECT COUNT(*) as cnt FROM products WHERE category_id = ? AND in_stock = 1",
152
+ (category_id,)
153
+ )
154
+ row = await cursor.fetchone()
155
+ total = row["cnt"]
156
+
157
+ # Mahsulotlarni olish
158
+ cursor = await db.execute(
159
+ """SELECT * FROM products WHERE category_id = ? AND in_stock = 1
160
+ ORDER BY created_at DESC LIMIT ? OFFSET ?""",
161
+ (category_id, per_page, offset)
162
+ )
163
+ rows = await cursor.fetchall()
164
+ products = [dict(r) for r in rows]
165
+
166
+ return products, total
167
+
168
+
169
+ async def get_product_by_id(product_id):
170
+ """ID bo'yicha mahsulot olish"""
171
+ async with aiosqlite.connect(DATABASE_PATH) as db:
172
+ db.row_factory = aiosqlite.Row
173
+ cursor = await db.execute("SELECT * FROM products WHERE id = ?", (product_id,))
174
+ row = await cursor.fetchone()
175
+ return dict(row) if row else None
176
+
177
+
178
+ async def get_all_products():
179
+ """Barcha mahsulotlarni olish"""
180
+ async with aiosqlite.connect(DATABASE_PATH) as db:
181
+ db.row_factory = aiosqlite.Row
182
+ cursor = await db.execute(
183
+ "SELECT p.*, c.name as category_name FROM products p LEFT JOIN categories c ON p.category_id = c.id ORDER BY p.created_at DESC"
184
+ )
185
+ rows = await cursor.fetchall()
186
+ return [dict(r) for r in rows]
187
+
188
+
189
+ async def update_product(product_id, **kwargs):
190
+ """Mahsulotni yangilash"""
191
+ allowed_fields = {"name", "description", "price", "category_id", "photo_id", "in_stock"}
192
+ fields = {k: v for k, v in kwargs.items() if k in allowed_fields}
193
+ if not fields:
194
+ return False
195
+
196
+ set_clause = ", ".join(f"{k} = ?" for k in fields)
197
+ values = list(fields.values()) + [product_id]
198
+
199
+ async with aiosqlite.connect(DATABASE_PATH) as db:
200
+ await db.execute(f"UPDATE products SET {set_clause} WHERE id = ?", values)
201
+ await db.commit()
202
+ return True
203
+
204
+
205
+ async def delete_product(product_id):
206
+ """Mahsulotni o'chirish"""
207
+ async with aiosqlite.connect(DATABASE_PATH) as db:
208
+ await db.execute("DELETE FROM products WHERE id = ?", (product_id,))
209
+ await db.commit()
210
+
211
+
212
+ async def search_products(query):
213
+ """Mahsulotlarni qidirish"""
214
+ async with aiosqlite.connect(DATABASE_PATH) as db:
215
+ db.row_factory = aiosqlite.Row
216
+ cursor = await db.execute(
217
+ "SELECT * FROM products WHERE in_stock = 1 AND (name LIKE ? OR description LIKE ?) ORDER BY created_at DESC",
218
+ (f"%{query}%", f"%{query}%")
219
+ )
220
+ rows = await cursor.fetchall()
221
+ return [dict(r) for r in rows]
222
+
223
+
224
+ # ==================== SAVAT (CART) ====================
225
+
226
+ async def add_to_cart(user_id, product_id, quantity=1):
227
+ """Savatga mahsulot qo'shish"""
228
+ async with aiosqlite.connect(DATABASE_PATH) as db:
229
+ # Mavjudligini tekshirish
230
+ cursor = await db.execute(
231
+ "SELECT * FROM cart_items WHERE user_id = ? AND product_id = ?",
232
+ (user_id, product_id)
233
+ )
234
+ existing = await cursor.fetchone()
235
+
236
+ if existing:
237
+ new_qty = existing[3] + quantity
238
+ if new_qty <= 0:
239
+ await db.execute(
240
+ "DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
241
+ (user_id, product_id)
242
+ )
243
+ else:
244
+ await db.execute(
245
+ "UPDATE cart_items SET quantity = ? WHERE user_id = ? AND product_id = ?",
246
+ (new_qty, user_id, product_id)
247
+ )
248
+ else:
249
+ if quantity > 0:
250
+ await db.execute(
251
+ "INSERT INTO cart_items (user_id, product_id, quantity) VALUES (?, ?, ?)",
252
+ (user_id, product_id, quantity)
253
+ )
254
+ await db.commit()
255
+
256
+
257
+ async def get_cart(user_id):
258
+ """Foydalanuvchi savatini olish"""
259
+ async with aiosqlite.connect(DATABASE_PATH) as db:
260
+ db.row_factory = aiosqlite.Row
261
+ cursor = await db.execute(
262
+ """SELECT ci.*, p.name, p.price, p.photo_id
263
+ FROM cart_items ci
264
+ JOIN products p ON ci.product_id = p.id
265
+ WHERE ci.user_id = ?""",
266
+ (user_id,)
267
+ )
268
+ rows = await cursor.fetchall()
269
+ return [dict(r) for r in rows]
270
+
271
+
272
+ async def get_cart_total(user_id):
273
+ """Savat umumiy summasini olish"""
274
+ async with aiosqlite.connect(DATABASE_PATH) as db:
275
+ cursor = await db.execute(
276
+ """SELECT COALESCE(SUM(ci.quantity * p.price), 0) as total
277
+ FROM cart_items ci
278
+ JOIN products p ON ci.product_id = p.id
279
+ WHERE ci.user_id = ?""",
280
+ (user_id,)
281
+ )
282
+ row = await cursor.fetchone()
283
+ return row[0]
284
+
285
+
286
+ async def update_cart_quantity(user_id, product_id, quantity):
287
+ """Savatdagi mahsulot miqdorini o'zgartirish"""
288
+ async with aiosqlite.connect(DATABASE_PATH) as db:
289
+ if quantity <= 0:
290
+ await db.execute(
291
+ "DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
292
+ (user_id, product_id)
293
+ )
294
+ else:
295
+ await db.execute(
296
+ "UPDATE cart_items SET quantity = ? WHERE user_id = ? AND product_id = ?",
297
+ (quantity, user_id, product_id)
298
+ )
299
+ await db.commit()
300
+
301
+
302
+ async def remove_from_cart(user_id, product_id):
303
+ """Savatdan mahsulotni o'chirish"""
304
+ async with aiosqlite.connect(DATABASE_PATH) as db:
305
+ await db.execute(
306
+ "DELETE FROM cart_items WHERE user_id = ? AND product_id = ?",
307
+ (user_id, product_id)
308
+ )
309
+ await db.commit()
310
+
311
+
312
+ async def clear_cart(user_id):
313
+ """Savatni tozalash"""
314
+ async with aiosqlite.connect(DATABASE_PATH) as db:
315
+ await db.execute("DELETE FROM cart_items WHERE user_id = ?", (user_id,))
316
+ await db.commit()
317
+
318
+
319
+ # ==================== USERS & BROADCAST ====================
320
+
321
+ async def add_user(user_id, username, full_name):
322
+ """Yangi foydalanuvchini qo'shish"""
323
+ async with aiosqlite.connect(DATABASE_PATH) as db:
324
+ await db.execute(
325
+ "INSERT OR IGNORE INTO users (user_id, username, full_name, joined_at) VALUES (?, ?, ?, ?)",
326
+ (user_id, username, full_name, time.time())
327
+ )
328
+ await db.commit()
329
+
330
+ async def get_all_users():
331
+ """Barcha foydalanuvchilarni olish"""
332
+ async with aiosqlite.connect(DATABASE_PATH) as db:
333
+ db.row_factory = aiosqlite.Row
334
+ cursor = await db.execute("SELECT user_id FROM users")
335
+ rows = await cursor.fetchall()
336
+ return [row["user_id"] for row in rows]
337
+
338
+ async def get_all_orders_with_items():
339
+ """Excel export uchun barcha buyurtmalar va mahsulotlar"""
340
+ async with aiosqlite.connect(DATABASE_PATH) as db:
341
+ db.row_factory = aiosqlite.Row
342
+ cursor = await db.execute("""
343
+ SELECT o.id, o.user_id, o.full_name, o.phone, o.address, o.total_price, o.status, o.created_at,
344
+ oi.product_name, oi.quantity, oi.price
345
+ FROM orders o
346
+ JOIN order_items oi ON o.id = oi.order_id
347
+ ORDER BY o.created_at DESC
348
+ """)
349
+ rows = await cursor.fetchall()
350
+ return [dict(r) for r in rows]
351
+
352
+ # ==================== BUYURTMALAR ====================
353
+
354
+ async def create_order(user_id, full_name, phone, address):
355
+ """Yangi buyurtma yaratish"""
356
+ cart = await get_cart(user_id)
357
+ if not cart:
358
+ return None
359
+
360
+ total = await get_cart_total(user_id)
361
+
362
+ async with aiosqlite.connect(DATABASE_PATH) as db:
363
+ cursor = await db.execute(
364
+ """INSERT INTO orders (user_id, full_name, phone, address, total_price, status, created_at)
365
+ VALUES (?, ?, ?, ?, ?, 'yangi', ?)""",
366
+ (user_id, full_name, phone, address, total, time.time())
367
+ )
368
+ order_id = cursor.lastrowid
369
+
370
+ # Savat elementlarini buyurtmaga ko'chirish
371
+ for item in cart:
372
+ await db.execute(
373
+ """INSERT INTO order_items (order_id, product_id, product_name, quantity, price)
374
+ VALUES (?, ?, ?, ?, ?)""",
375
+ (order_id, item["product_id"], item["name"], item["quantity"], item["price"])
376
+ )
377
+
378
+ await db.commit()
379
+
380
+ # Savatni tozalash
381
+ await clear_cart(user_id)
382
+ return order_id
383
+
384
+
385
+ async def get_order(order_id):
386
+ """Buyurtmani olish"""
387
+ async with aiosqlite.connect(DATABASE_PATH) as db:
388
+ db.row_factory = aiosqlite.Row
389
+ cursor = await db.execute("SELECT * FROM orders WHERE id = ?", (order_id,))
390
+ row = await cursor.fetchone()
391
+ return dict(row) if row else None
392
+
393
+
394
+ async def get_order_items(order_id):
395
+ """Buyurtma elementlarini olish"""
396
+ async with aiosqlite.connect(DATABASE_PATH) as db:
397
+ db.row_factory = aiosqlite.Row
398
+ cursor = await db.execute(
399
+ "SELECT * FROM order_items WHERE order_id = ?", (order_id,)
400
+ )
401
+ rows = await cursor.fetchall()
402
+ return [dict(r) for r in rows]
403
+
404
+
405
+ async def get_user_orders(user_id):
406
+ """Foydalanuvchi buyurtmalarini olish"""
407
+ async with aiosqlite.connect(DATABASE_PATH) as db:
408
+ db.row_factory = aiosqlite.Row
409
+ cursor = await db.execute(
410
+ "SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC",
411
+ (user_id,)
412
+ )
413
+ rows = await cursor.fetchall()
414
+ return [dict(r) for r in rows]
415
+
416
+
417
+ async def update_order_status(order_id, status):
418
+ """Buyurtma statusini o'zgartirish"""
419
+ async with aiosqlite.connect(DATABASE_PATH) as db:
420
+ await db.execute(
421
+ "UPDATE orders SET status = ? WHERE id = ?",
422
+ (status, order_id)
423
+ )
424
+ await db.commit()
425
+
426
+
427
+ async def get_all_orders(status=None):
428
+ """Barcha buyurtmalarni olish (admin uchun)"""
429
+ async with aiosqlite.connect(DATABASE_PATH) as db:
430
+ db.row_factory = aiosqlite.Row
431
+ if status:
432
+ cursor = await db.execute(
433
+ "SELECT * FROM orders WHERE status = ? ORDER BY created_at DESC",
434
+ (status,)
435
+ )
436
+ else:
437
+ cursor = await db.execute(
438
+ "SELECT * FROM orders ORDER BY created_at DESC LIMIT 50"
439
+ )
440
+ rows = await cursor.fetchall()
441
+ return [dict(r) for r in rows]
442
+
443
+
444
+ # ==================== STATISTIKA ====================
445
+
446
+ async def get_stats():
447
+ """Umumiy statistika (admin uchun)"""
448
+ async with aiosqlite.connect(DATABASE_PATH) as db:
449
+ stats = {}
450
+
451
+ cursor = await db.execute("SELECT COUNT(*) FROM products WHERE in_stock = 1")
452
+ row = await cursor.fetchone()
453
+ stats["products_count"] = row[0]
454
+
455
+ cursor = await db.execute("SELECT COUNT(*) FROM orders")
456
+ row = await cursor.fetchone()
457
+ stats["orders_count"] = row[0]
458
+
459
+ cursor = await db.execute("SELECT COUNT(*) FROM orders WHERE status = 'yangi'")
460
+ row = await cursor.fetchone()
461
+ stats["new_orders"] = row[0]
462
+
463
+ cursor = await db.execute(
464
+ "SELECT COALESCE(SUM(total_price), 0) FROM orders WHERE status != 'bekor_qilindi'"
465
+ )
466
+ row = await cursor.fetchone()
467
+ stats["total_revenue"] = row[0]
468
+
469
+ cursor = await db.execute("SELECT COUNT(DISTINCT user_id) FROM orders")
470
+ row = await cursor.fetchone()
471
+ stats["unique_customers"] = row[0]
472
+
473
+ return stats
requirements.txt ADDED
Binary file (5.99 kB). View file
 
verify_db.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import asyncio
3
+ import aiosqlite
4
+ from config import DATABASE_PATH
5
+ import sys
6
+
7
+ # Windows console encoding fix
8
+ sys.stdout.reconfigure(encoding='utf-8')
9
+
10
+ async def verify():
11
+ if not DATABASE_PATH:
12
+ print("Error: DATABASE_PATH not set")
13
+ return
14
+
15
+ try:
16
+ async with aiosqlite.connect(DATABASE_PATH) as db:
17
+ print(f"Connected to {DATABASE_PATH}")
18
+
19
+ tables = ["categories", "products", "cart_items", "orders", "order_items"]
20
+ all_exist = True
21
+ for table in tables:
22
+ cursor = await db.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table}';")
23
+ result = await cursor.fetchone()
24
+ if result:
25
+ print(f"[OK] Table '{table}' exists.")
26
+ else:
27
+ print(f"[MISSING] Table '{table}' NOT FOUND!")
28
+ all_exist = False
29
+
30
+ # Check if default categories were inserted
31
+ cursor = await db.execute("SELECT COUNT(*) FROM categories")
32
+ count = await cursor.fetchone()
33
+ print(f"[INFO] Categories count: {count[0]}")
34
+
35
+ if all_exist and count[0] > 0:
36
+ print("SUCCESS: Database verification passed.")
37
+ else:
38
+ print("FAILURE: Database verification failed.")
39
+
40
+ except Exception as e:
41
+ print(f"ERROR: {e}")
42
+
43
+ if __name__ == "__main__":
44
+ asyncio.run(verify())