From 2eee7619099e245022a654ad197cc09054746a71 Mon Sep 17 00:00:00 2001 From: MpakobecEx Date: Wed, 27 May 2026 23:52:06 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8E?= =?UTF-8?q?=20QR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 71 ++++++++++++++++++++++++++++++++++++----------------- qr_utils.py | 27 +++++++------------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/bot.py b/bot.py index 197a04c..89524eb 100644 --- a/bot.py +++ b/bot.py @@ -1,15 +1,17 @@ +import base64 import os import asyncio import sqlite3 from functools import wraps from dotenv import load_dotenv from aiogram import Bot, Dispatcher, types -from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, MenuButtonWebApp +from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, MenuButtonWebApp, \ + BufferedInputFile from aiogram.filters import Command from aiohttp import web import database as db from phone_utils import normalize_phone -from qr_utils import decode_qr_from_image, generate_qr_code +from qr_utils import decode_qr_from_image, generate_qr_code, generate_qr_base64, generate_qr_image # Загружаем переменные из .env load_dotenv() @@ -80,27 +82,34 @@ async def cmd_get(message: Message): record_type = record['type'] phone = record.get('phone') - qr_code = record.get('qr_code') + qr_code_base64 = record.get('qr_code') qr_data = record.get('qr_data') - if record_type == 'both' and qr_code: - await message.answer_photo( - photo=qr_code, - caption=f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🃏 Бонусная карта привязана\n🔄 Использован: {record['usage_count']} раз", - parse_mode="Markdown" - ) + if (record_type == 'both' or record_type == 'qr') and qr_code_base64: + # Конвертируем base64 в байты для отправки + if qr_code_base64.startswith('data:image/png;base64,'): + img_base64 = qr_code_base64.replace('data:image/png;base64,', '') + img_bytes = base64.b64decode(img_base64) + photo_file = BufferedInputFile(img_bytes, filename="qr.png") + + if record_type == 'both': + await message.answer_photo( + photo=photo_file, + caption=f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🃏 Бонусная карта\n🔄 Использован: {record['usage_count']} раз", + parse_mode="Markdown" + ) + else: + short_data = qr_data[:50] + '...' if qr_data and len(qr_data) > 50 else (qr_data or '—') + await message.answer_photo( + photo=photo_file, + caption=f"🎲 *МракоЗвон*\n🃏 Бонусная карта\n📝 Данные: `{short_data}`\n🔄 Использована: {record['usage_count']} раз", + parse_mode="Markdown" + ) elif record_type == 'phone': await message.answer( f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🔄 Использован: {record['usage_count']} раз", parse_mode="Markdown" ) - elif record_type == 'qr' and qr_code: - short_data = qr_data[:50] + '...' if qr_data and len(qr_data) > 50 else (qr_data or '—') - await message.answer_photo( - photo=qr_code, - caption=f"🎲 *МракоЗвон*\n🃏 Бонусная карта\n📝 Данные: `{short_data}`\n🔄 Использована: {record['usage_count']} раз", - parse_mode="Markdown" - ) @dp.message(Command("stats")) @@ -234,7 +243,8 @@ async def cmd_update_menu(message: Message): async def handle_message(message: Message): phone = None qr_raw_data = None - qr_image = None + qr_image_bytes = None + qr_image_base64 = None # 1. Распознаём QR из фото if message.photo: @@ -242,8 +252,11 @@ async def handle_message(message: Message): 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) + # Генерируем новый чистый QR из распознанных данных + qr_image_bytes = generate_qr_image(qr_raw_data) + qr_image_base64 = generate_qr_base64(qr_raw_data) # 2. Извлекаем номер из текста if message.text: @@ -251,15 +264,27 @@ async def handle_message(message: Message): # 3. Определяем тип записи и сохраняем if phone and qr_raw_data: - db.add_record('both', phone=phone, qr_code=qr_image, qr_data=qr_raw_data) - await message.answer(f"✅ Сохранено: номер `{phone}` + бонусная карта", parse_mode="Markdown") + # Сохраняем base64 для веб-морды и байты для Telegram + db.add_record('both', phone=phone, qr_code=qr_image_base64, qr_data=qr_raw_data) + + # Отправляем сгенерированный QR в ответ + photo_file = BufferedInputFile(qr_image_bytes, filename="qr.png") + await message.answer_photo( + photo=photo_file, + caption=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: - db.add_record('qr', qr_code=qr_image, qr_data=qr_raw_data) - short_data = qr_raw_data[:50] + '...' if len(qr_raw_data) > 50 else qr_raw_data - await message.answer(f"🃏 Сохранена бонусная карта: `{short_data}`", parse_mode="Markdown") + db.add_record('qr', qr_code=qr_image_base64, qr_data=qr_raw_data) + photo_file = BufferedInputFile(qr_image_bytes, filename="qr.png") + await message.answer_photo( + photo=photo_file, + caption=f"🃏 Сохранена бонусная карта\n📝 Данные: `{qr_raw_data[:50]}...`", + parse_mode="Markdown" + ) else: await message.answer("⚠️ Не распознано. Отправь номер, QR-код или оба вместе.") diff --git a/qr_utils.py b/qr_utils.py index 86f2fe1..9ad9ae3 100644 --- a/qr_utils.py +++ b/qr_utils.py @@ -7,33 +7,21 @@ import qrcode def decode_qr_from_image(image_data: bytes) -> str | None: - """ - Распознаёт QR-код из изображения - Возвращает декодированную строку или None - """ + """Распознаёт QR-код из изображения""" try: - # Конвертируем байты в numpy массив nparr = np.frombuffer(image_data, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) - - # Инициализируем детектор QR-кодов detector = cv2.QRCodeDetector() - - # Декодируем data, bbox, _ = detector.detectAndDecode(img) - if data: return data.strip() except Exception as e: print(f"QR decode error: {e}") - return None -def generate_qr_code(data: str, size: int = 300) -> str: - """ - Генерирует QR-код из строки и возвращает base64 строку - """ +def generate_qr_image(data: str) -> bytes: + """Генерирует QR-код и возвращает байты изображения""" qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, @@ -44,10 +32,13 @@ def generate_qr_code(data: str, size: int = 300) -> str: qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") - - # Конвертируем в base64 buffered = BytesIO() img.save(buffered, format="PNG") - img_base64 = base64.b64encode(buffered.getvalue()).decode() + return buffered.getvalue() + +def generate_qr_base64(data: str) -> str: + """Генерирует QR и возвращает base64 строку для веб-морды""" + img_bytes = generate_qr_image(data) + img_base64 = base64.b64encode(img_bytes).decode() return f"data:image/png;base64,{img_base64}" \ No newline at end of file