File size: 21,992 Bytes
540c414
dbbc0da
 
3e50d1b
540c414
 
 
 
90b8920
21d6b00
540c414
afad0a7
 
 
21d6b00
 
 
75f90e3
dbbc0da
 
3e4d69e
540c414
 
fb3841b
dbbc0da
bfb4d97
8f2df11
dbbc0da
afad0a7
bfb4d97
 
 
afad0a7
bfb4d97
dbbc0da
21d6b00
afad0a7
 
4a8a9a2
 
bfb4d97
90b8920
 
 
4a8a9a2
21d6b00
 
90b8920
dbbc0da
 
21d6b00
 
 
3d53618
21d6b00
 
dbbc0da
afad0a7
 
 
 
 
 
 
 
 
bfb4d97
afad0a7
bfb4d97
afad0a7
 
 
 
 
 
 
 
 
 
 
 
 
bfb4d97
afad0a7
bfb4d97
afad0a7
 
3d53618
 
 
 
 
 
 
 
 
 
 
afad0a7
dbbc0da
 
bfb4d97
540c414
 
3e4d69e
540c414
 
 
 
 
90b8920
 
 
 
 
 
 
540c414
 
 
 
dbbc0da
bfb4d97
540c414
 
3e4d69e
dbbc0da
8b29853
 
90b8920
 
 
 
 
 
 
 
 
 
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3d53618
bfb4d97
90b8920
bfb4d97
 
 
dbbc0da
540c414
 
dbbc0da
2e8fce9
dbbc0da
540c414
dbbc0da
 
 
 
 
bfb4d97
dbbc0da
bfb4d97
540c414
 
 
 
 
 
90b8920
 
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
dbbc0da
540c414
 
90b8920
 
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbbc0da
540c414
 
dbbc0da
2e8fce9
dbbc0da
540c414
dbbc0da
 
 
 
 
 
bfb4d97
dbbc0da
bfb4d97
540c414
dbbc0da
3d53618
dbbc0da
 
 
 
75f90e3
bfb4d97
75f90e3
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75f90e3
dbbc0da
 
75f90e3
90b8920
 
 
 
bfb4d97
90b8920
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bfb4d97
 
 
90b8920
 
 
 
 
75f90e3
bfb4d97
 
90b8920
 
 
 
 
 
bfb4d97
90b8920
 
 
 
 
 
75f90e3
90b8920
 
 
 
 
 
 
 
 
 
 
bfb4d97
90b8920
 
 
 
 
 
 
 
 
 
 
 
 
75f90e3
 
bfb4d97
 
 
 
 
 
 
 
 
 
75f90e3
 
bfb4d97
75f90e3
90b8920
 
bfb4d97
 
 
 
90b8920
75f90e3
 
 
 
dbbc0da
bfb4d97
 
dd12782
 
21d6b00
90b8920
21d6b00
 
bfb4d97
dd12782
 
 
21d6b00
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3d53618
bfb4d97
21d6b00
 
 
dd12782
 
 
21d6b00
75f90e3
21d6b00
bfb4d97
21d6b00
 
 
 
dbbc0da
90b8920
 
 
 
 
 
 
bfb4d97
90b8920
 
 
 
 
 
 
 
 
 
bfb4d97
90b8920
 
 
 
 
bfb4d97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540c414
 
 
dbbc0da
bfb4d97
dbbc0da
 
a8bc99d
 
bfb4d97
3d53618
 
 
 
a8bc99d
 
 
bfb4d97
a8bc99d
90b8920
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
import asyncio
import json
import os
import urllib.parse
from datetime import datetime
from aiogram import Bot, Dispatcher, types, F
from aiogram.filters import Command
from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder
from flask import Flask, request, jsonify, render_template_string, redirect
import logging
import threading
from huggingface_hub import HfApi, hf_hub_download
from huggingface_hub.utils import RepositoryNotFoundError
from werkzeug.utils import secure_filename

# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Инициализация бота и Flask
BOT_TOKEN = '7595736142:AAHSU3WGItBkebIgjO293J2WjX5qWAne8Y8'
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
app = Flask(__name__)

# Путь для хранения данных
DATA_FILE = 'data2.json'

# Настройки Hugging Face
REPO_ID = "flpolprojects/Clients"
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")

# Функции для работы с данными
def load_data():
    try:
        download_db_from_hf()
        with open(DATA_FILE, 'r', encoding='utf-8') as f:
            loaded_data = json.load(f)
        if not (isinstance(loaded_data, dict) and 'products' in loaded_data and 'orders' in loaded_data):
            logger.error("Неверная структура JSON файла")
            loaded_data = {'products': [], 'orders': []}
        if "categories" not in loaded_data:
            loaded_data["categories"] = []
        return loaded_data
    except Exception as e:
        logger.error(f"Ошибка при загрузке данных: {e}")
        return {'products': [], 'orders': [], 'categories': []}

def save_data(data):
    try:
        with open(DATA_FILE, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=4)
        # upload_db_to_hf() убрано отсюда
    except Exception as e:
        logger.error(f"Ошибка при сохранении данных: {e}")

def upload_db_to_hf():
    try:
        api = HfApi()
        api.upload_file(
            path_or_fileobj=DATA_FILE,
            path_in_repo=DATA_FILE,
            repo_id=REPO_ID,
            repo_type="dataset",
            token=HF_TOKEN_WRITE,
            commit_message=f"Автоматическое резервное копирование {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        )
        logger.info("База загружена на Hugging Face")
    except Exception as e:
        logger.error(f"Ошибка при загрузке резервной копии: {e}")

def download_db_from_hf():
    try:
        hf_hub_download(
            repo_id=REPO_ID,
            filename=DATA_FILE,
            repo_type="dataset",
            token=HF_TOKEN_READ,
            local_dir=".",
            local_dir_use_symlinks=False
        )
        logger.info("База скачана из Hugging Face")
    except Exception as e:
        logger.error(f"Ошибка при скачивании: {e}")
        raise

# Периодическое копирование каждые 30 секунд
def start_periodic_backup():
    def backup_loop():
        upload_db_to_hf()
        # Запускаем следующий вызов через 30 секунд
        threading.Timer(30, backup_loop).start()

    # Запускаем первый вызов
    threading.Timer(30, backup_loop).start()
    logger.info("Периодическое копирование каждые 30 секунд запущено")

# Загрузка данных
data = load_data()

# Формирование клавиатур
def get_main_keyboard():
    builder = ReplyKeyboardBuilder()
    builder.button(text="📋 Каталог")
    builder.button(text="🛒 Корзина")
    builder.button(text="📦 Заказы")
    builder.adjust(2)
    return builder.as_markup(resize_keyboard=True)

def get_category_keyboard():
    builder = InlineKeyboardBuilder()
    for category in data['categories']:
        builder.button(text=category['name'], callback_data=f"cat_{category['id']}")
    builder.adjust(2)
    return builder.as_markup()

def get_product_keyboard(product_id):
    builder = InlineKeyboardBuilder()
    builder.button(text="Добавить в корзину", callback_data=f"add_{product_id}")
    return builder.as_markup()

# Обработчики бота
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
    await message.answer("Здравствуйте ! это магазин Routine!. Выберите действие:", reply_markup=get_main_keyboard())

# Изменено условие на "📋 Каталог", чтобы соответствовать кнопке
@dp.message(F.text == "📋 Каталог")
async def show_categories(message: types.Message):
    if not data['categories']:
        await message.answer("Нет доступных категорий.")
        return
    await message.answer("Выберите категорию:", reply_markup=get_category_keyboard())

@dp.callback_query(F.data.startswith("cat_"))
async def show_products_in_category(callback_query: types.CallbackQuery):
    try:
        cat_id = int(callback_query.data.split('_')[1])
        products_in_cat = [p for p in data['products'] if p.get('category_id') == cat_id]
        
        if not products_in_cat:
            await bot.send_message(callback_query.from_user.id, "В этой категории нет товаров.")
            await bot.answer_callback_query(callback_query.id)
            return

        async def send_product_batch(products_batch):
            for product in products_batch:
                photo_url = f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/photos/{product['photo']}" if product.get('photo') else None
                caption = f"🏷 {product['name']} - {product['price']} сом\nОписание: {product['description']}\n/id: {product['id']}"
                try:
                    if photo_url:
                        await bot.send_photo(chat_id=callback_query.from_user.id, photo=photo_url, caption=caption, reply_markup=get_product_keyboard(product['id']))
                    else:
                        await bot.send_message(callback_query.from_user.id, caption, reply_markup=get_product_keyboard(product['id']))
                except Exception as e:
                    logger.error(f"Ошибка при отправке: {e}")
                    await bot.send_message(callback_query.from_user.id, caption, reply_markup=get_product_keyboard(product['id']))

        batch_size = 5
        for i in range(0, len(products_in_cat), batch_size):
            batch = products_in_cat[i:i + batch_size]
            await send_product_batch(batch)
            await asyncio.sleep(0.1)
            
        await bot.answer_callback_query(callback_query.id)
    except Exception as e:
        logger.error(f"Ошибка в show_products_in_category: {e}")
        await bot.answer_callback_query(callback_query.id, "Ошибка при загрузке товаров")

@dp.message(F.text == "🛒 Корзина")
async def show_cart(message: types.Message):
    user_id = message.from_user.id
    cart = next((o for o in data['orders'] if o['user_id'] == user_id and not o.get('completed')), None)
    if not cart or not cart['items']:
        await message.answer("Ваша корзина пуста.")
        return
    total = 0
    response = "Ваша корзина:\n"
    for item in cart['items']:
        product = next(p for p in data['products'] if p['id'] == item['product_id'])
        response += f"🏷 {product['name']} - {product['price']} сом x {item['quantity']}\n"
        total += product['price'] * item['quantity']
    response += f"\nИтого: {total} сом"
    builder = InlineKeyboardBuilder()
    builder.button(text="Оформить заказ", callback_data=f"complete_{user_id}")
    await message.answer(response, reply_markup=builder.as_markup())

@dp.callback_query(F.data.startswith("add_"))
async def add_to_cart(callback_query: types.CallbackQuery):
    try:
        product_id = int(callback_query.data.split('_')[1])
        product = next((p for p in data['products'] if p['id'] == product_id), None)
        if product:
            user_id = callback_query.from_user.id
            cart = next((o for o in data['orders'] if o['user_id'] == user_id and not o.get('completed')), None)
            if not cart:
                cart = {'user_id': user_id, 'items': [], 'date': datetime.now().isoformat()}
                data['orders'].append(cart)
            cart['items'].append({'product_id': product_id, 'quantity': 1})
            save_data(data)
            await bot.answer_callback_query(callback_query.id, "Товар добавлен в корзину!")
    except Exception as e:
        logger.error(f"Ошибка при добавлении в корзину: {e}")
        await bot.answer_callback_query(callback_query.id, "Ошибка")

@dp.callback_query(F.data.startswith("complete_"))
async def complete_order(callback_query: types.CallbackQuery):
    try:
        user_id = int(callback_query.data.split('_')[1])
        cart = next((o for o in data['orders'] if o['user_id'] == user_id and not o.get('completed')), None)
        if cart and cart['items']:
            total = 0
            cart_text = "Привет, я хочу сделать заказ:\n"
            for item in cart['items']:
                product = next((p for p in data['products'] if p['id'] == item['product_id']), None)
                if product:
                    cart_text += f"{product['name']} - {product['price']} сом x {item['quantity']}\n"
                    total += product['price'] * item['quantity']
            cart_text += f"\nИтого: {total} сом"
            encoded_text = urllib.parse.quote(cart_text)
            whatsapp_link = f"https://wa.me/996709513331?text={encoded_text}"
            data['orders'].remove(cart)
            save_data(data)
            await bot.send_message(user_id, f"Оформите заказ через WhatsApp:\n{whatsapp_link}")
            await bot.answer_callback_query(callback_query.id)
    except Exception as e:
        logger.error(f"Ошибка при оформлении заказа: {e}")
        await bot.answer_callback_query(callback_query.id, "Ошибка")

@dp.message(F.text == "📦 Заказы")
async def show_orders(message: types.Message):
    user_id = message.from_user.id
    user_orders = [o for o in data['orders'] if o.get('completed')]
    if not user_orders:
        await message.answer("У вас нет оформленных заказов.")
        return
    for order in user_orders:
        response = "Ваш заказ:\n"
        total = 0
        for item in order['items']:
            product = next(p for p in data['products'] if p['id'] == item['product_id'])
            response += f"🏷 {product['name']} - {product['price']} сом x {item['quantity']}\n"
            total += product['price'] * item['quantity']
        response += f"\nИтого: {total} сом\nДата: {order['date']}"
        await message.answer(response)

# Админ-панель
admin_html = """
<!DOCTYPE html>
<html>
<head>
    <title>Админ-панель</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 10px;
            background-color: #f0f0f0;
        }
        .container {
            max-width: 1000px;
            margin: 0 auto;
        }
        .section {
            background-color: #fff;
            padding: 10px;
            margin-bottom: 15px;
            border-radius: 5px;
        }
        h1, h2, h3 {
            margin: 10px 0;
        }
        input, textarea, select {
            width: 100%;
            margin: 5px 0;
            padding: 8px;
            box-sizing: border-box;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 8px 12px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            width: 100%;
            margin: 5px 0;
        }
        button:hover {
            background-color: #45a049;
        }
        img {
            max-width: 100%;
            height: auto;
            max-height: 150px;
        }
        .item {
            border: 1px solid #ccc;
            padding: 10px;
            margin: 5px 0;
            word-wrap: break-word;
        }
        @media (max-width: 600px) {
            .section {
                padding: 8px;
            }
            button {
                padding: 6px 10px;
            }
            h1 {
                font-size: 1.5em;
            }
            h2 {
                font-size: 1.2em;
            }
            h3 {
                font-size: 1em;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Админ-панель</h1>
        <div class="section">
            <h2>Управление категориями</h2>
            <form id="addCategoryForm" method="POST" action="/add_category">
                <input type="text" name="name" placeholder="Название категории" required>
                <button type="submit">Добавить категорию</button>
            </form>
            <h3>Существующие категории</h3>
            {% if categories %}
                {% for category in categories %}
                <div class="item">
                    {{ category.name }} (ID: {{ category.id }})
                    <button onclick="deleteCategory({{ category.id }})">Удалить</button>
                </div>
                {% endfor %}
            {% else %}
                <p>Нет категорий.</p>
            {% endif %}
        </div>
        <div class="section">
            <h2>Управление товарами</h2>
            <form id="addProductForm" method="POST" enctype="multipart/form-data" action="/add_product">
                <input type="text" name="name" placeholder="Название" required>
                <input type="number" name="price" placeholder="Цена" step="0.01" required>
                <textarea name="description" placeholder="Описание" required></textarea>
                <label>Категория:</label>
                <select name="category_id" required>
                    <option value="">Выберите категорию</option>
                    {% for category in categories %}
                    <option value="{{ category.id }}">{{ category.name }}</option>
                    {% endfor %}
                </select>
                <input type="file" name="photo" accept="image/*">
                <button type="submit">Добавить товар</button>
            </form>
            <h3>Существующие товары</h3>
            {% if products %}
                {% for product in products %}
                <div class="item">
                    {{ product.name }} - {{ product.price }} сом<br>
                    {{ product.description }}<br>
                    {% if product.photo %}
                        <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product.photo }}" alt="{{ product.name }}">
                    {% endif %}
                    <button onclick="deleteProduct({{ product.id }})">Удалить</button>
                </div>
                {% endfor %}
            {% else %}
                <p>Нет товаров.</p>
            {% endif %}
        </div>
        <div class="section">
            <h2>Заказы</h2>
            {% if orders %}
                {% for order in orders %}
                <div class="item">
                    Пользователь: {{ order.user_id }}<br>
                    Дата: {{ order.date }}<br>
                    Товары:
                    {% for item in order['items'] %}
                        {% for product in products %}
                            {% if product.id == item.product_id %}
                                {{ item.quantity }} x {{ product.name }}<br>
                            {% endif %}
                        {% endfor %}
                    {% endfor %}
                </div>
                {% endfor %}
            {% else %}
                <p>Нет заказов.</p>
            {% endif %}
        </div>
    </div>
    <script>
        const eventSource = new EventSource('/updates');
        eventSource.onmessage = function(event) {
            if (event.data === 'update') {
                window.location.reload();
            }
        };
        eventSource.onerror = function() {
            console.log("Ошибка SSE, reconnecting...");
        };

        async function deleteProduct(productId) {
            const response = await fetch(`/delete_product/${productId}`, { method: 'POST' });
            if (response.ok) broadcastUpdate();
        }
        async function deleteCategory(categoryId) {
            const response = await fetch(`/delete_category/${categoryId}`, { method: 'POST' });
            if (response.ok) broadcastUpdate();
        }
        function broadcastUpdate() {
            fetch('/broadcast_update', { method: 'POST' });
        }
    </script>
</body>
</html>
"""

update_event = threading.Event()

@app.route('/')
def admin_panel():
    try:
        return render_template_string(admin_html, products=data['products'], orders=data['orders'], categories=data['categories'], repo_id=REPO_ID)
    except Exception as e:
        logger.error(f"Ошибка в шаблоне: {e}")
        return "Ошибка сервера", 500

@app.route('/add_product', methods=['POST'])
def add_product():
    try:
        name = request.form['name']
        price = float(request.form['price'])
        description = request.form['description']
        category_id = int(request.form['category_id'])
        photo = request.files.get('photo')
        product_id = max((p['id'] for p in data['products']), default=0) + 1

        photo_filename = None
        if photo and photo.filename:
            photo_filename = secure_filename(photo.filename)
            temp_path = os.path.join(".", photo_filename)
            photo.save(temp_path)
            api = HfApi()
            api.upload_file(
                path_or_fileobj=temp_path,
                path_in_repo=f"photos/{photo_filename}",
                repo_id=REPO_ID,
                repo_type="dataset",
                token=HF_TOKEN_WRITE,
                commit_message=f"Добавлено фото для товара {name}"
            )
            os.remove(temp_path)

        data['products'].append({
            'id': product_id,
            'name': name,
            'price': price,
            'description': description,
            'category_id': category_id,
            'photo': photo_filename
        })
        save_data(data)
        update_event.set()
        return redirect("/")
    except Exception as e:
        logger.error(f"Ошибка при добавлении товара: {e}")
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/delete_product/<int:product_id>', methods=['POST'])
def delete_product(product_id):
    try:
        data['products'] = [p for p in data['products'] if p['id'] != product_id]
        save_data(data)
        update_event.set()
        return jsonify({'status': 'success'})
    except Exception as e:
        logger.error(f"Ошибка при удалении товара: {e}")
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/add_category', methods=['POST'])
def add_category():
    try:
        name = request.form['name']
        category_id = max((c['id'] for c in data['categories']), default=0) + 1
        data['categories'].append({'id': category_id, 'name': name})
        save_data(data)
        update_event.set()
        return redirect("/")
    except Exception as e:
        logger.error(f"Ошибка при добавлении категории: {e}")
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/delete_category/<int:category_id>', methods=['POST'])
def delete_category(category_id):
    try:
        data['categories'] = [c for c in data['categories'] if c['id'] != category_id]
        save_data(data)
        update_event.set()
        return jsonify({'status': 'success'})
    except Exception as e:
        logger.error(f"Ошибка при удалении категории: {e}")
        return jsonify({'status': 'error', 'message': str(e)}), 500

@app.route('/updates')
def sse_updates():
    def stream():
        while True:
            update_event.wait()
            yield "data: update\n\n"
            update_event.clear()
    return app.response_class(stream(), mimetype="text/event-stream")

@app.route('/broadcast_update', methods=['POST'])
def broadcast_update():
    update_event.set()
    return jsonify({'status': 'success'})

# Запуск
async def on_startup(_):
    logger.info("Бот запущен!")

def run_flask():
    app.run(host='0.0.0.0', port=7860, debug=True, use_reloader=False)

if __name__ == '__main__':
    flask_thread = threading.Thread(target=run_flask, daemon=True)
    flask_thread.start()
    logger.info("Flask запущен")
    
    # Запуск периодического копирования
    start_periodic_backup()

    try:
        asyncio.run(dp.start_polling(bot, on_startup=on_startup))
    except KeyboardInterrupt:
        logger.info("Остановка")
    finally:
        flask_thread.join()