Files

181 lines
8.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Обработчик согласия на обработку персональных данных.
Управляет жизненным циклом согласия:
- запрос согласия → да/нет
- отзыв согласия (consent_revoked_date)
- повторное согласие после отказа
"""
import datetime
import re
import logging
from sqlalchemy import select
from app.database import async_session
from app.models import BotUser, BotConversation, BotMessage
from app.max_api import max_api
from app.config_reader import config_reader
from app.keyboards import contact_keyboard, consent_keyboard, return_to_consent_keyboard
logger = logging.getLogger(__name__)
def _matches_any(text: str, words: list) -> bool:
"""
Проверяет, содержит ли текст хотя бы одно из указанных слов.
Использует split для точного совпадения коротких слов (≤3 символов)
и подстроку для длинных (>3 символов).
"""
text_words = set(text.split())
for w in words:
if w in text_words:
return True
if len(w) > 3 and w in text:
return True
return False
async def handle_consent_callback(user_id: int, conv_id: int, callback_id: str) -> None:
"""Маршрутизация callback-кнопок согласия (да/нет)."""
if callback_id == "consent_yes":
await handle_consent_yes(user_id, conv_id)
elif callback_id == "consent_no":
await handle_consent_no(user_id, conv_id)
async def handle_consent_response(user_id: int, conv_id: int, text: str) -> None:
"""Обработка текстового ответа на запрос согласия (да/нет/другое)."""
text_lower = text.strip().lower()
consent_words = ["да", "даю", "согласен", "согласна", "yes", "ok", "хорошо", "даю согласие"]
refuse_words = ["нет", "не даю", "отказ", "no", "не согласен", "не согласна"]
if _matches_any(text_lower, consent_words):
await handle_consent_yes(user_id, conv_id)
elif _matches_any(text_lower, refuse_words):
await handle_consent_no(user_id, conv_id)
else:
await max_api.send_message(
user_id,
"Пожалуйста, дайте чёткий ответ — даёте ли вы согласие на обработку "
"персональных данных? Это необходимо для обработки вашего запроса.",
attachments=consent_keyboard(),
conversation_id=conv_id,
)
async def handle_consent_yes(user_id: int, conv_id: int) -> None:
"""Обработка согласия: устанавливает consent_given=True, переводит в awaiting_contact."""
async with async_session() as db:
result = await db.execute(select(BotUser).where(BotUser.id == user_id))
user = result.scalar_one_or_none()
if user:
user.consent_given = True
user.consent_date = datetime.datetime.utcnow()
result = await db.execute(
select(BotConversation).where(BotConversation.id == conv_id)
)
conv = result.scalar_one_or_none()
if conv:
conv.state = "awaiting_contact"
await db.commit()
try:
await max_api.send_message(
user_id,
"Спасибо! Поделитесь, пожалуйста, вашим контактом, чтобы мы могли "
"связаться с вами.\n\n"
"Нажмите кнопку «Поделиться контактом» или введите номер телефона / email вручную.",
attachments=contact_keyboard(),
conversation_id=conv_id,
)
except Exception as e:
logger.error(f"Failed to send consent_yes message: {e}")
async def handle_consent_no(user_id: int, conv_id: int) -> None:
"""Обработка отказа: устанавливает consent_refused, показывает контакты."""
async with async_session() as db:
result = await db.execute(
select(BotConversation).where(BotConversation.id == conv_id)
)
conv = result.scalar_one_or_none()
if conv:
conv.state = "consent_refused"
await db.commit()
phones = config_reader.get_phones()
email = config_reader.get_site_mail()
phone_text = "\n".join(f"📞 {phone}" for phone, _ in phones)
try:
await max_api.send_message(
user_id,
f"Без вашего согласия мы не можем обработать обращение.\n\n"
f"Вы можете позвонить нам:\n{phone_text}\n\n"
f"📧 {email}\n\n"
f"Если передумаете и захотите дать согласие — просто напишите об этом.",
conversation_id=conv_id,
)
except Exception as e:
logger.error(f"Failed to send consent_no message: {e}")
async def handle_refused_again(user_id: int, conv_id: int, text: str) -> None:
"""Повторная обработка после отказа: позволяет дать согласие или показывает контакты."""
text_lower = text.strip().lower()
consent_words = ["да", "даю", "согласен", "согласна", "yes", "ok", "хорошо", "даю согласие", "передумал", "согласен дать"]
refuse_words = ["нет", "не даю", "не хочу", "отказ", "отмена", "no", "cancel", "я отказался"]
if _matches_any(text_lower, consent_words):
await handle_consent_yes(user_id, conv_id)
elif _matches_any(text_lower, refuse_words):
phones = config_reader.get_phones()
email = config_reader.get_site_mail()
phone_text = "\n".join(f"📞 {phone}" for phone, _ in phones)
try:
await max_api.send_message(
user_id,
f"Хорошо, без согласия мы не можем обработать обращение.\n\n"
f"Вы можете позвонить нам:\n{phone_text}\n\n"
f"📧 {email}\n\n"
f"Если передумаете — напишите «Даю согласие».",
conversation_id=conv_id,
)
except Exception as e:
logger.error(f"Failed to send refused_again message: {e}")
else:
phones = config_reader.get_phones()
email = config_reader.get_site_mail()
phone_text = "\n".join(f"📞 {phone}" for phone, _ in phones)
try:
await max_api.send_message(
user_id,
f"Хорошо, без согласия мы не можем обработать обращение.\n\n"
f"Вы можете позвонить нам:\n{phone_text}\n\n"
f"📧 {email}\n\n"
f"Если передумаете — напишите «Даю согласие».",
conversation_id=conv_id,
)
except Exception as e:
logger.error(f"Failed to send refused_again message: {e}")
async def handle_revoke_consent(user_id: int, conv_id: int) -> None:
"""Отзыв согласия: устанавливает consent_given=False, consent_revoked_date=now."""
async with async_session() as db:
user = await db.get(BotUser, user_id)
if user and user.consent_given:
user.consent_given = False
from app.handlers.greeting import moscow_now
user.consent_revoked_date = moscow_now()
await db.commit()
await max_api.send_message(
user_id,
"Понял вас. Ваше согласие на обработку персональных данных отозвано. "
"Если передумаете — напишите «Даю согласие».",
conversation_id=conv_id,
)