From 4492c0359017303f36226e6f4db579ab283597c8 Mon Sep 17 00:00:00 2001 From: "oleg.vodyanov91@gmail.com" Date: Sun, 1 Jun 2025 22:38:34 +0400 Subject: [PATCH] update register logic --- .gitignore | 2 +- src/core/enums/dialog_helpers.py | 11 ++ src/docbot/conversations.pkl | Bin 1116 -> 0 bytes .../handlers/doctors/register_handler.py | 141 +++++++++++++++--- src/docbot/main.py | 4 +- src/docbot/services/admins_service.py | 4 +- src/docbot/services/doctors_service.py | 4 +- src/docbot/services/referral_service.py | 9 +- src/docbot/services/session_service.py | 4 +- 9 files changed, 142 insertions(+), 37 deletions(-) create mode 100644 src/core/enums/dialog_helpers.py delete mode 100644 src/docbot/conversations.pkl diff --git a/.gitignore b/.gitignore index 73e2f82..a86e3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ alembic.ini alembic/ db/ __pycache__ -conversations.pkl \ No newline at end of file +src/docbot/conversations.pkl \ No newline at end of file diff --git a/src/core/enums/dialog_helpers.py b/src/core/enums/dialog_helpers.py new file mode 100644 index 0000000..03a032f --- /dev/null +++ b/src/core/enums/dialog_helpers.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class ConfirmationMessage(str, Enum): + PROCEED = "Продолжаем 😊" + DECLINE = "Отмена 😔" + + +class Acknowledgement(str, Enum): + OK = "Понятно 😊" + DECLINE = "Отмена 😔" diff --git a/src/docbot/conversations.pkl b/src/docbot/conversations.pkl deleted file mode 100644 index 438e566253bf8abc1d66d13ae54baeb5525e846e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1116 zcmZ8h-Afcv6mN5Ncl{_ev4~PB0)<%A%902ah03Lc3XL9OxHEItdviY4d+$gF8!Bj7 z7ZN=E7rg{YCRs1N7PGJY0YT79PrXHFW@pWuhq-6Y*FER=J7<;}U)@e0ik}Mf>o&{x z7O0R!ao>|r0ewo)SuPb7hV5I*7sh@MbeQ_p{^U~pT7yDLN}UU2Tn;&A`+dE)Ar}hG#l$McC@r@zJTKt?y`J{@w=>1RJgo)c$(xMv#gyRt8vV_**I1^#!gg#oqP%G6rMfntaqLDu`!(v0xVFcr>AdD)}wU; z_Y^$u_0jt=5A9*LSx^9AA|ykvoJhnNmEN;qf)IeY;6wB-w$r(WR7JZ^^!oEja5 zS}fzZ=+=pSN-SAl3pc9E;dZ!NU9B#KAH!XgUWD)asw?4ExKmxOuJrYXZ%}^M7k&!2 z&}R$JZH(Cow+CV@iOwO9#8P~L!ZUW^Ke+$Y;LhS=#VZued3*f>7ucrJ_7zfWm=%I( rCR&o^jGJXMLA=iu2z1V3L>*k_J!{tV_l)@KAdX`aYp&*A4Dx>g&&0{{ diff --git a/src/docbot/handlers/doctors/register_handler.py b/src/docbot/handlers/doctors/register_handler.py index 791c08f..c0c09f3 100644 --- a/src/docbot/handlers/doctors/register_handler.py +++ b/src/docbot/handlers/doctors/register_handler.py @@ -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, # если используете хранение состояний ) diff --git a/src/docbot/main.py b/src/docbot/main.py index bb51501..cae516c 100644 --- a/src/docbot/main.py +++ b/src/docbot/main.py @@ -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") diff --git a/src/docbot/services/admins_service.py b/src/docbot/services/admins_service.py index 4e3abaa..7263efc 100644 --- a/src/docbot/services/admins_service.py +++ b/src/docbot/services/admins_service.py @@ -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() \ No newline at end of file diff --git a/src/docbot/services/doctors_service.py b/src/docbot/services/doctors_service.py index 6852707..ef4d85f 100644 --- a/src/docbot/services/doctors_service.py +++ b/src/docbot/services/doctors_service.py @@ -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, diff --git a/src/docbot/services/referral_service.py b/src/docbot/services/referral_service.py index 43ca072..dacf7a6 100644 --- a/src/docbot/services/referral_service.py +++ b/src/docbot/services/referral_service.py @@ -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 ) diff --git a/src/docbot/services/session_service.py b/src/docbot/services/session_service.py index ba97c77..79c597c 100644 --- a/src/docbot/services/session_service.py +++ b/src/docbot/services/session_service.py @@ -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)