diff --git a/bot.py b/bot.py index c76f210..fe9056c 100644 --- a/bot.py +++ b/bot.py @@ -72,29 +72,30 @@ async def cmd_start(message: Message): @dp.message(Command("get")) @allowed_only async def cmd_get(message: Message): - result = db.get_random_from_oldest() - if not result: - await message.answer("❌ База пуста, брат. Добавь номера!") + record = db.get_random_record() + if not record: + await message.answer("❌ База пуста, брат!") return - phone = result['phone'] - bonus_card = result.get('bonus_card') + record_type = record['type'] + phone = record.get('phone') + qr_code = record.get('qr_code') - if bonus_card: + if record_type == 'both' and qr_code: await message.answer_photo( - photo=bonus_card, - caption=f"🎲 *МракоЗвон*\n" - f"📞 Номер: `{phone}`\n" - f"🃏 Бонусная карта привязана\n" - f"🔄 Использован: {result['usage_count']} раз", + photo=qr_code, + caption=f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🃏 Бонусная карта привязана\n🔄 Использован: {record['usage_count']} раз", parse_mode="Markdown" ) - else: + elif record_type == 'phone': await message.answer( - f"🎲 *МракоЗвон*\n" - f"📞 `{phone}`\n" - f"🔄 Использован: {result['usage_count']} раз\n" - f"❌ Бонусная карта отсутствует", + f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🔄 Использован: {record['usage_count']} раз", + parse_mode="Markdown" + ) + elif record_type == 'qr' and qr_code: + await message.answer_photo( + photo=qr_code, + caption=f"🎲 *МракоЗвон*\n🃏 Бонусная карта\n🔄 Использована: {record['usage_count']} раз", parse_mode="Markdown" ) @@ -217,104 +218,61 @@ async def cmd_whitelist(message: Message): async def handle_message(message: Message): phone = None qr_raw_data = None + qr_image = None - # 1. Если есть фото - пытаемся распознать QR + # 1. Распознаём QR из фото if message.photo: photo = message.photo[-1] file = await bot.get_file(photo.file_id) file_bytes = await bot.download_file(file.file_path) qr_raw_data = decode_qr_from_image(file_bytes.read()) + if qr_raw_data: + qr_image = generate_qr_code(qr_raw_data) - # 2. Если есть текст - пробуем извлечь номер + # 2. Извлекаем номер из текста if message.text: phone = normalize_phone(message.text.strip()) - # 3. Если нет ничего - if not phone and not qr_raw_data: - await message.answer( - "⚠️ Не распознал.\n\n" - "Отправь:\n" - "• номер телефона текстом\n" - "• бонусную карту (QR-код)\n" - "• номер + карту вместе" - ) - return - - # 4. Только номер - if phone and not qr_raw_data: - if db.add_number(phone): - await message.answer(f"✅ Сохранён номер: `{phone}`", parse_mode="Markdown") - else: - await message.answer(f"⚠️ Номер `{phone}` уже есть в базе", parse_mode="Markdown") - return - - # 5. Только QR (без номера) - if qr_raw_data and not phone: - await message.answer( - f"🃏 Распознана бонусная карта:\n" - f"`{qr_raw_data[:50]}{'...' if len(qr_raw_data) > 50 else ''}`\n\n" - f"⚠️ Чтобы привязать карту к номеру, отправь и номер тоже.\n" - f"Пример: номер и карту в одном сообщении" - ) - return - - # 6. Номер + QR вместе + # 3. Определяем тип записи и сохраняем if phone and qr_raw_data: - qr_base64 = generate_qr_code(qr_raw_data) - - if db.add_number(phone, qr_base64): - await message.answer( - f"✅ Сохранено:\n" - f"📞 Номер: `{phone}`\n" - f"🃏 Бонусная карта привязана", - parse_mode="Markdown" - ) - else: - # Если номер уже есть - обновляем карту - if db.update_bonus_card(phone, qr_base64): - await message.answer( - f"🔄 Обновлена бонусная карта для номера `{phone}`", - parse_mode="Markdown" - ) - else: - await message.answer(f"⚠️ Ошибка сохранения", parse_mode="Markdown") - return + # Тип: номер + QR + db.add_record('both', phone=phone, qr_code=qr_image, qr_data=qr_raw_data) + await message.answer(f"✅ Сохранено: номер `{phone}` + бонусная карта", parse_mode="Markdown") + elif phone: + # Тип: только номер + db.add_record('phone', phone=phone) + await message.answer(f"✅ Сохранён номер: `{phone}`", parse_mode="Markdown") + elif qr_raw_data: + # Тип: только QR + db.add_record('qr', qr_code=qr_image, qr_data=qr_raw_data) + await message.answer(f"🃏 Сохранена бонусная карта: `{qr_raw_data[:50]}...`", parse_mode="Markdown") + else: + await message.answer("⚠️ Не распознано. Отправь номер, QR-код или оба вместе.") # ========== ВЕБ-СЕРВЕР ========== routes = web.RouteTableDef() -@routes.get("/get_webapp") -async def serve_webapp(request): - try: - with open("web_app.html", "r", encoding="utf-8") as f: - html = f.read() - return web.Response(text=html, content_type="text/html") - except FileNotFoundError: - return web.Response(text="web_app.html not found", status=404) +@routes.get("/get_records") +async def get_records(request): + records = db.get_all_records() + return web.json_response({"records": records}) +@routes.get("/get_qr_code/{record_id}") +async def get_qr_code(request): + record_id = int(request.match_info.get('record_id', 0)) + with sqlite3.connect(DB_NAME) as conn: + conn.row_factory = sqlite3.Row + result = conn.execute("SELECT qr_code FROM records WHERE id = ?", (record_id,)).fetchone() + if result and result['qr_code']: + return web.json_response({"qr_code": result['qr_code']}) + return web.json_response({"qr_code": None}) -@routes.get("/get_numbers") -async def get_numbers(request): - numbers = db.get_all_numbers() - safe_numbers = [] - for num in numbers: - safe_numbers.append({ - "id": num["id"], - "phone": num["phone"], - "has_bonus_card": bool(num.get("bonus_card")), - "created_at": num["created_at"], - "used_at": num["used_at"], - "usage_count": num["usage_count"] - }) - return web.json_response({"numbers": safe_numbers}) - - -@routes.post("/delete_number") -async def delete_number(request): +@routes.post("/delete_record") +async def delete_record(request): data = await request.json() - db.delete_number(data["id"]) + db.delete_record(data["id"]) return web.json_response({"status": "ok"}) diff --git a/database.py b/database.py index a033094..64bb924 100644 --- a/database.py +++ b/database.py @@ -7,19 +7,21 @@ DB_NAME = "bot.db" def init_db(): with sqlite3.connect(DB_NAME) as conn: - # Основная таблица с номерами и бонусными картами + # Единая таблица для всех записей conn.execute(""" - CREATE TABLE IF NOT EXISTS numbers ( + CREATE TABLE IF NOT EXISTS records ( id INTEGER PRIMARY KEY AUTOINCREMENT, - phone TEXT UNIQUE NOT NULL, - bonus_card TEXT, + type TEXT NOT NULL, + phone TEXT, + qr_code TEXT, + qr_data TEXT, created_at TEXT NOT NULL, used_at TEXT, usage_count INTEGER DEFAULT 0 ) """) - # Таблица белого списка пользователей + # Таблица белого списка conn.execute(""" CREATE TABLE IF NOT EXISTS allowed_users ( user_id INTEGER PRIMARY KEY, @@ -29,89 +31,66 @@ def init_db(): ) """) - conn.execute("CREATE INDEX IF NOT EXISTS idx_created ON numbers(created_at)") - conn.execute("CREATE INDEX IF NOT EXISTS idx_used ON numbers(used_at)") + # Индексы + conn.execute("CREATE INDEX IF NOT EXISTS idx_records_used ON records(used_at)") + conn.execute("CREATE INDEX IF NOT EXISTS idx_records_type ON records(type)") -# ========== Функции для работы с номерами и бонусными картами ========== -def add_number(phone: str, bonus_card: str = None) -> bool: - """Добавляет номер и опционально бонусную карту""" +def add_record(record_type: str, phone: str = None, qr_code: str = None, qr_data: str = None) -> bool: + """Добавляет запись""" with sqlite3.connect(DB_NAME) as conn: try: - conn.execute( - "INSERT INTO numbers (phone, bonus_card, created_at) VALUES (?, ?, ?)", - (phone, bonus_card, datetime.now().isoformat()) - ) + conn.execute(""" + INSERT INTO records (type, phone, qr_code, qr_data, created_at) + VALUES (?, ?, ?, ?, ?) + """, (record_type, phone, qr_code, qr_data, datetime.now().isoformat())) return True except sqlite3.IntegrityError: return False -def update_bonus_card(phone: str, bonus_card: str) -> bool: - """Обновляет бонусную карту для существующего номера""" - with sqlite3.connect(DB_NAME) as conn: - cursor = conn.execute( - "UPDATE numbers SET bonus_card = ? WHERE phone = ?", - (bonus_card, phone) - ) - return cursor.rowcount > 0 - - -def get_random_from_oldest(): +def get_random_record(): + """Возвращает случайную запись""" with sqlite3.connect(DB_NAME) as conn: conn.row_factory = sqlite3.Row - total_count = conn.execute("SELECT COUNT(*) as count FROM numbers").fetchone()["count"] + total_count = conn.execute("SELECT COUNT(*) as count FROM records").fetchone()["count"] if total_count <= 50: limit = 10 else: limit = max(10, min(50, int(total_count * 0.2))) - oldest = conn.execute( - "SELECT *, COALESCE(used_at, created_at) as sort_date FROM numbers ORDER BY sort_date ASC LIMIT ?", - (limit,) - ).fetchall() + oldest = conn.execute(""" + SELECT *, COALESCE(used_at, created_at) as sort_date + FROM records + ORDER BY sort_date ASC + LIMIT ? + """, (limit,)).fetchall() if not oldest: return None selected = random.choice(oldest) - conn.execute( - "UPDATE numbers SET usage_count = usage_count + 1, used_at = ? WHERE id = ?", - (datetime.now().isoformat(), selected["id"]) - ) + conn.execute(""" + UPDATE records SET usage_count = usage_count + 1, used_at = ? + WHERE id = ? + """, (datetime.now().isoformat(), selected["id"])) return dict(selected) -def get_all_numbers(): +def get_all_records(): with sqlite3.connect(DB_NAME) as conn: conn.row_factory = sqlite3.Row - rows = conn.execute("SELECT * FROM numbers ORDER BY created_at DESC").fetchall() + rows = conn.execute("SELECT * FROM records ORDER BY created_at DESC").fetchall() return [dict(row) for row in rows] -def delete_number(record_id: int): +def delete_record(record_id: int): with sqlite3.connect(DB_NAME) as conn: - conn.execute("DELETE FROM numbers WHERE id = ?", (record_id,)) - - -def get_bonus_card(record_id: int) -> str | None: - """Получить бонусную карту по ID записи""" - with sqlite3.connect(DB_NAME) as conn: - conn.row_factory = sqlite3.Row - result = conn.execute("SELECT bonus_card FROM numbers WHERE id = ?", (record_id,)).fetchone() - return result["bonus_card"] if result else None - - -def get_bonus_card_by_phone(phone: str) -> str | None: - """Получить бонусную карту по номеру телефона""" - with sqlite3.connect(DB_NAME) as conn: - conn.row_factory = sqlite3.Row - result = conn.execute("SELECT bonus_card FROM numbers WHERE phone = ?", (phone,)).fetchone() - return result["bonus_card"] if result else None + conn.execute("DELETE FROM records WHERE id = ?", (record_id,)) # ========== Функции для белого списка ========== diff --git a/web_app.html b/web_app.html index 61e2a1e..17eec30 100644 --- a/web_app.html +++ b/web_app.html @@ -3,7 +3,7 @@
-