update register logic

This commit is contained in:
oleg.vodyanov91@gmail.com 2025-06-01 22:38:34 +04:00
parent 44d04256a2
commit 4492c03590
9 changed files with 142 additions and 37 deletions

2
.gitignore vendored
View File

@ -6,4 +6,4 @@ alembic.ini
alembic/
db/
__pycache__
conversations.pkl
src/docbot/conversations.pkl

View File

@ -0,0 +1,11 @@
from enum import Enum
class ConfirmationMessage(str, Enum):
PROCEED = "Продолжаем 😊"
DECLINE = "Отмена 😔"
class Acknowledgement(str, Enum):
OK = "Понятно 😊"
DECLINE = "Отмена 😔"

Binary file not shown.

View File

@ -10,15 +10,45 @@ from telegram.ext import (
from docbot.handlers.utils.cancel_handler import get_cancel_handler
from docbot.services.referral_service import validate_referral_code, mark_referral_code_as_used
from core.enums.consultation_types import Consultation
from core.enums.dialog_helpers import ConfirmationMessage, Acknowledgement
ASK_REFERRAL_CODE = 1
ASK_NAME = 2
ASK_CONSULTATION_TYPE = 3
SEND_ACKNOWLEDGEMENT_INFO = 2
ASK_NAME = 3
ASK_SPECIALITY = 4
SEND_DIPLOMA_ACK_INFO = 5
SEND_CONSULTATION_TYPE_ACK_INFO = 6
SEND_ME_REFERRAL_CODE_TEXT = (
"📝 Пожалуйста, пришлите реферальный код, чтобы начать процесс регистрации."
)
ACCEPT_OFFER_TEXT = (
"📝 Продолжая работу с ботом, вы соглашаетесь с условиями оферты и на обработку персональных данных! \n"
"Оферта и согласие - это гиперссылки на Яндекс документ, которые ведут на страничку с текстом"
)
SEND_ME_YOUR_FULL_NAME_TEXT = (
"📝 Пожалуйста, введите ФИО."
)
SEND_ME_YOUR_SPECIALITY_TEXT = (
"📝 Пожалуйста, введите вашу специальность, в соответствии с которой планируете проводить консультации."
)
WAIT_FOR_ACTIVATION_TEXT = (
"📝 Заявка принята, направьте диплом и аккредитацию с указанием кода в теме письма на адрес электронной почты:____\n"
"В этом шаге нужно встроить подсказку: в каком формате направлять диплом, как скачать с госуслуг\n"
"выписку об аккредитации. Верификация займет 24 часа."
)
VERIFICATION_IN_PROGRESS_TEXT = (
"📝 Пока идет верификация, вы можете ознакомиться с форматами работы и условиями сервиса\n"
"Формат: название форматов + пакеты."
)
ALL_INFORMATION_RECEIVED_TEXT = (
"✅ Спасибо за информацию, мы с вами свяжемся. До свидания. 👋"
)
async def ask_doctor_referral_code(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
await update.message.reply_text(
"📝 Пожалуйста, пришлите реферальный код.",
SEND_ME_REFERRAL_CODE_TEXT,
parse_mode="Markdown"
)
return ASK_REFERRAL_CODE
@ -27,6 +57,12 @@ async def ask_doctor_referral_code(update: Update, context: ContextTypes.DEFAULT
async def receive_doctor_referral_code(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_referral_code = update.message.text.strip()
ref_obj = await validate_referral_code(doctor_referral_code)
reply_keyboard = [
[
ConfirmationMessage.PROCEED.value,
ConfirmationMessage.DECLINE.value
]
]
if not ref_obj:
return ConversationHandler.END
@ -35,8 +71,25 @@ async def receive_doctor_referral_code(update: Update, context: ContextTypes.DEF
context.user_data["doctor_telegram_id"] = doctor_telegram_id
context.user_data["ref_obj"] = ref_obj
await update.message.reply_text(
"📝 Пожалуйста, пришлите своё имя или псевдоним.",
parse_mode="Markdown"
ACCEPT_OFFER_TEXT,
parse_mode="Markdown",
reply_markup=ReplyKeyboardMarkup(
reply_keyboard, one_time_keyboard=True, input_field_placeholder="Оферта и согласие"
),
)
return SEND_ACKNOWLEDGEMENT_INFO
async def receive_doctor_info_confirmation(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_info_confirmation = update.message.text.strip()
if doctor_info_confirmation == ConfirmationMessage.DECLINE.value:
return ConversationHandler.END
await update.message.reply_text(
SEND_ME_YOUR_FULL_NAME_TEXT,
parse_mode="Markdown",
reply_markup=ReplyKeyboardRemove()
)
return ASK_NAME
@ -44,37 +97,72 @@ async def receive_doctor_referral_code(update: Update, context: ContextTypes.DEF
async def receive_doctor_name(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_name = update.message.text.strip()
context.user_data["doctor_name"] = doctor_name
await update.message.reply_text(
SEND_ME_YOUR_SPECIALITY_TEXT,
parse_mode="Markdown"
)
return ASK_SPECIALITY
async def receive_doctor_speciality(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_speciality = update.message.text.strip()
context.user_data["doctor_speciality"] = doctor_speciality
reply_keyboard = [
[
Consultation.ASYNC_TEXT.value,
Consultation.ASYNC_TEXT_WITH_DIALOG.value,
Consultation.ONLINE_CHAT.value,
Consultation.AUDION_CALL.value,
Consultation.VIDEO_CALL.value
Acknowledgement.OK,
Acknowledgement.DECLINE
]
]
await update.message.reply_text(
f"Пожалуйста, выберите тип консультации.",
WAIT_FOR_ACTIVATION_TEXT,
parse_mode="Markdown",
reply_markup=ReplyKeyboardMarkup(
reply_keyboard, one_time_keyboard=True, input_field_placeholder="Тип консультации"
reply_keyboard, one_time_keyboard=True, input_field_placeholder="Отправка пакета документов"
),
)
return ASK_CONSULTATION_TYPE
return SEND_DIPLOMA_ACK_INFO
async def receive_doctor_consultation_type(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_consultation_type = update.message.text.strip()
async def receive_doctor_diploma_acknowledgement_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_info_confirmation = update.message.text.strip()
reply_keyboard = [
[
Acknowledgement.OK,
Acknowledgement.DECLINE
]
]
if doctor_info_confirmation == Acknowledgement.DECLINE.value:
return ConversationHandler.END
await update.message.reply_text(
"📝 Спасибо, сохраняю информацию.",
VERIFICATION_IN_PROGRESS_TEXT,
parse_mode="Markdown",
reply_markup=ReplyKeyboardRemove()
reply_markup=ReplyKeyboardMarkup(
reply_keyboard, one_time_keyboard=True, input_field_placeholder="Ознакомление с форматами работы"
),
)
return SEND_CONSULTATION_TYPE_ACK_INFO
async def receive_doctor_consultation_packages_acknowledgement_status(update: Update,
context: ContextTypes.DEFAULT_TYPE) -> int:
doctor_info_confirmation = update.message.text.strip()
if doctor_info_confirmation == Acknowledgement.DECLINE.value:
return ConversationHandler.END
await update.message.reply_text(
ALL_INFORMATION_RECEIVED_TEXT,
parse_mode="Markdown"
)
await mark_referral_code_as_used(context.user_data["ref_obj"], context.user_data["doctor_telegram_id"],
context.user_data["doctor_name"], Consultation(doctor_consultation_type))
context.user_data["doctor_name"])
return ConversationHandler.END
@ -83,21 +171,30 @@ def ask_doctor_info_handler() -> CommandHandler:
return CommandHandler("register", ask_doctor_referral_code)
def get_register_doctor_handler() -> ConversationHandler:
def get_register_doctor_first_stage_handler() -> ConversationHandler:
return ConversationHandler(
entry_points=[ask_doctor_info_handler()],
states={
ASK_REFERRAL_CODE: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_referral_code)
],
SEND_ACKNOWLEDGEMENT_INFO: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_info_confirmation)
],
ASK_NAME: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_name)
],
ASK_CONSULTATION_TYPE: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_consultation_type)
ASK_SPECIALITY: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_speciality)
],
SEND_DIPLOMA_ACK_INFO: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_diploma_acknowledgement_status)
],
SEND_CONSULTATION_TYPE_ACK_INFO: [
MessageHandler(filters.TEXT & ~filters.COMMAND, receive_doctor_consultation_packages_acknowledgement_status)
],
},
fallbacks=[get_cancel_handler()],
name="register_doctor_conversation", # для тестов/логирования
name="register_doctor_conversation_first_stage", # для тестов/логирования
persistent=True, # если используете хранение состояний
)

View File

@ -5,7 +5,7 @@ from core.config import settings
from docbot.handlers.start_handler import get_start_handler
from docbot.handlers.patients.send_form_handler import get_send_form_handler
from docbot.handlers.admins.doctors_handler import get_doctors_handler
from docbot.handlers.doctors.register_handler import get_register_doctor_handler
from docbot.handlers.doctors.register_handler import get_register_doctor_first_stage_handler
from docbot.handlers.admins.generate_ref import get_referral_handlers
from docbot.handlers.utils.unknown import get_unknown_handler
@ -24,7 +24,7 @@ def main():
app.add_handler(get_start_handler())
app.add_handler(get_send_form_handler())
app.add_handler(get_doctors_handler())
app.add_handler(get_register_doctor_handler())
app.add_handler(get_register_doctor_first_stage_handler())
app.add_handler(get_referral_handlers())
app.add_handler(get_unknown_handler())
logger.debug("Все хэндлеры зарегистрированы, запускаем polling")

View File

@ -9,7 +9,7 @@ async def get_admin_info(telegram_id: int) -> Admins | None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(Admins.telegram_id)
.where(Admins.telegram_id.match(str(telegram_id)))
.where(Admins.telegram_id.match(telegram_id))
)
return result.scalar_one_or_none()
@ -18,6 +18,6 @@ async def mark_doctor_inactive(telegram_id: int) -> Admins | None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(Admins.telegram_id)
.where(Admins.telegram_id.match(str(telegram_id)))
.where(Admins.telegram_id.match(telegram_id))
)
return result.scalar_one_or_none()

View File

@ -27,11 +27,11 @@ async def get_doctors_names() -> Doctors | None:
return result.all()
async def add_doctor(telegram_id: str, name: str, available_formats: Consultation, is_active: bool):
async def add_doctor(telegram_id: int, name: str, available_formats: Consultation, is_active: bool):
async with AsyncSessionLocal() as session:
async with session.begin():
session.add(Doctors(
telegram_id=str(telegram_id),
telegram_id=telegram_id,
name=name,
available_formats=available_formats,
is_active=is_active,

View File

@ -6,7 +6,6 @@ from sqlalchemy import select
from db.session import AsyncSessionLocal
from db.models import ReferralCode, Doctors
from core.enums.consultation_types import Consultation
async def generate_referral_code(length: int = 12) -> str:
@ -46,8 +45,7 @@ async def validate_referral_code(referral_code: str) -> ReferralCode | None:
return result.scalar_one_or_none()
async def mark_referral_code_as_used(referral_obj: ReferralCode, telegram_id: str, name: str,
available_formats: Consultation) -> Doctors:
async def mark_referral_code_as_used(referral_obj: ReferralCode, telegram_id: int, name: str) -> Doctors:
"""
Помечает referral_obj как использованный, создаёт запись в таблице Doctors
с привязкой к telegram_id и возвращает объект Doctor.
@ -60,10 +58,9 @@ async def mark_referral_code_as_used(referral_obj: ReferralCode, telegram_id: st
# создаём врача
new_doc = Doctors(
telegram_id=str(telegram_id),
telegram_id=telegram_id,
name=name,
available_formats=[Consultation(available_formats)],
is_active=True,
is_active=False,
created_at=datetime.utcnow(),
referral=referral_obj
)

View File

@ -19,7 +19,7 @@ async def create_session_code(telegram_id: int, form_link: str) -> str:
async with session.begin():
session.add(SessionCode(
code=code,
telegram_id=str(telegram_id), # храним как строку
telegram_id=telegram_id, # храним как строку
form_link=form_link,
sent_at=datetime.utcnow()
))
@ -51,7 +51,7 @@ async def get_pending_session(telegram_id: int) -> SessionCode | None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(SessionCode)
.where(SessionCode.telegram_id.match(str(telegram_id)))
.where(SessionCode.telegram_id.match(telegram_id))
.where(SessionCode.consulted_at.is_(None))
.order_by(SessionCode.sent_at.desc())
.limit(1)