Поправил генерацию QR

This commit is contained in:
MpakobecEx 2026-05-27 23:52:06 +05:00
parent f43caa7489
commit 2eee761909
2 changed files with 57 additions and 41 deletions

65
bot.py
View File

@ -1,15 +1,17 @@
import base64
import os import os
import asyncio import asyncio
import sqlite3 import sqlite3
from functools import wraps from functools import wraps
from dotenv import load_dotenv from dotenv import load_dotenv
from aiogram import Bot, Dispatcher, types 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 aiogram.filters import Command
from aiohttp import web from aiohttp import web
import database as db import database as db
from phone_utils import normalize_phone 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 # Загружаем переменные из .env
load_dotenv() load_dotenv()
@ -80,13 +82,27 @@ async def cmd_get(message: Message):
record_type = record['type'] record_type = record['type']
phone = record.get('phone') phone = record.get('phone')
qr_code = record.get('qr_code') qr_code_base64 = record.get('qr_code')
qr_data = record.get('qr_data') qr_data = record.get('qr_data')
if record_type == 'both' and qr_code: 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( await message.answer_photo(
photo=qr_code, photo=photo_file,
caption=f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🃏 Бонусная карта привязана\n🔄 Использован: {record['usage_count']} раз", 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" parse_mode="Markdown"
) )
elif record_type == 'phone': elif record_type == 'phone':
@ -94,13 +110,6 @@ async def cmd_get(message: Message):
f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🔄 Использован: {record['usage_count']} раз", f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🔄 Использован: {record['usage_count']} раз",
parse_mode="Markdown" 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")) @dp.message(Command("stats"))
@ -234,7 +243,8 @@ async def cmd_update_menu(message: Message):
async def handle_message(message: Message): async def handle_message(message: Message):
phone = None phone = None
qr_raw_data = None qr_raw_data = None
qr_image = None qr_image_bytes = None
qr_image_base64 = None
# 1. Распознаём QR из фото # 1. Распознаём QR из фото
if message.photo: if message.photo:
@ -242,8 +252,11 @@ async def handle_message(message: Message):
file = await bot.get_file(photo.file_id) file = await bot.get_file(photo.file_id)
file_bytes = await bot.download_file(file.file_path) file_bytes = await bot.download_file(file.file_path)
qr_raw_data = decode_qr_from_image(file_bytes.read()) qr_raw_data = decode_qr_from_image(file_bytes.read())
if qr_raw_data: 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. Извлекаем номер из текста # 2. Извлекаем номер из текста
if message.text: if message.text:
@ -251,15 +264,27 @@ async def handle_message(message: Message):
# 3. Определяем тип записи и сохраняем # 3. Определяем тип записи и сохраняем
if phone and qr_raw_data: if phone and qr_raw_data:
db.add_record('both', phone=phone, qr_code=qr_image, qr_data=qr_raw_data) # Сохраняем base64 для веб-морды и байты для Telegram
await message.answer(f"✅ Сохранено: номер `{phone}` + бонусная карта", parse_mode="Markdown") 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: elif phone:
db.add_record('phone', phone=phone) db.add_record('phone', phone=phone)
await message.answer(f"✅ Сохранён номер: `{phone}`", parse_mode="Markdown") await message.answer(f"✅ Сохранён номер: `{phone}`", parse_mode="Markdown")
elif qr_raw_data: elif qr_raw_data:
db.add_record('qr', qr_code=qr_image, qr_data=qr_raw_data) db.add_record('qr', qr_code=qr_image_base64, qr_data=qr_raw_data)
short_data = qr_raw_data[:50] + '...' if len(qr_raw_data) > 50 else qr_raw_data photo_file = BufferedInputFile(qr_image_bytes, filename="qr.png")
await message.answer(f"🃏 Сохранена бонусная карта: `{short_data}`", parse_mode="Markdown") await message.answer_photo(
photo=photo_file,
caption=f"🃏 Сохранена бонусная карта\n📝 Данные: `{qr_raw_data[:50]}...`",
parse_mode="Markdown"
)
else: else:
await message.answer("⚠️ Не распознано. Отправь номер, QR-код или оба вместе.") await message.answer("⚠️ Не распознано. Отправь номер, QR-код или оба вместе.")

View File

@ -7,33 +7,21 @@ import qrcode
def decode_qr_from_image(image_data: bytes) -> str | None: def decode_qr_from_image(image_data: bytes) -> str | None:
""" """Распознаёт QR-код из изображения"""
Распознаёт QR-код из изображения
Возвращает декодированную строку или None
"""
try: try:
# Конвертируем байты в numpy массив
nparr = np.frombuffer(image_data, np.uint8) nparr = np.frombuffer(image_data, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# Инициализируем детектор QR-кодов
detector = cv2.QRCodeDetector() detector = cv2.QRCodeDetector()
# Декодируем
data, bbox, _ = detector.detectAndDecode(img) data, bbox, _ = detector.detectAndDecode(img)
if data: if data:
return data.strip() return data.strip()
except Exception as e: except Exception as e:
print(f"QR decode error: {e}") print(f"QR decode error: {e}")
return None return None
def generate_qr_code(data: str, size: int = 300) -> str: def generate_qr_image(data: str) -> bytes:
""" """Генерирует QR-код и возвращает байты изображения"""
Генерирует QR-код из строки и возвращает base64 строку
"""
qr = qrcode.QRCode( qr = qrcode.QRCode(
version=1, version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L, 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) qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white") img = qr.make_image(fill_color="black", back_color="white")
# Конвертируем в base64
buffered = BytesIO() buffered = BytesIO()
img.save(buffered, format="PNG") 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}" return f"data:image/png;base64,{img_base64}"