Поправил генерацию QR
This commit is contained in:
parent
f43caa7489
commit
2eee761909
71
bot.py
71
bot.py
|
|
@ -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,27 +82,34 @@ 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:
|
||||||
await message.answer_photo(
|
# Конвертируем base64 в байты для отправки
|
||||||
photo=qr_code,
|
if qr_code_base64.startswith('data:image/png;base64,'):
|
||||||
caption=f"🎲 *МракоЗвон*\n📞 Номер: `{phone}`\n🃏 Бонусная карта привязана\n🔄 Использован: {record['usage_count']} раз",
|
img_base64 = qr_code_base64.replace('data:image/png;base64,', '')
|
||||||
parse_mode="Markdown"
|
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':
|
elif record_type == 'phone':
|
||||||
await message.answer(
|
await message.answer(
|
||||||
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-код или оба вместе.")
|
||||||
|
|
||||||
|
|
|
||||||
27
qr_utils.py
27
qr_utils.py
|
|
@ -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}"
|
||||||
Loading…
Reference in New Issue