update logic, add time zones, add new db fields, send payment link

This commit is contained in:
o.vodianov 2025-08-24 23:27:02 +04:00
parent 547cac174d
commit dfb3de958b
7 changed files with 113 additions and 10 deletions

View File

@ -2,6 +2,7 @@ import uuid
import hashlib import hashlib
import base64 import base64
import re import re
import pytz
from datetime import datetime from datetime import datetime
from urllib.parse import urlparse from urllib.parse import urlparse
@ -11,7 +12,7 @@ def UUID_code_generator(code_length: int) -> str:
Генерирует уникальный код в формате UUID. Генерирует уникальный код в формате UUID.
Возвращает строку с кодом. Возвращает строку с кодом.
""" """
return str(uuid.uuid4().hex[:{code_length}].upper()) return str(uuid.uuid4().hex[:code_length].upper())
def generate_session_code(telegram_id: int, phone: str, consultation_date_time: str) -> str: def generate_session_code(telegram_id: int, phone: str, consultation_date_time: str) -> str:
@ -40,6 +41,22 @@ def is_phone_correct(phone: str) -> bool:
return re.match(regex, normalized) return re.match(regex, normalized)
def get_timezones():
return_value = {}
for tz in pytz.common_timezones:
c = tz.split("/")
if len(c) > 1:
if c[0] not in return_value.keys():
return_value[c[0]] = []
return_value[c[0]].append(c[1])
for i in ["GMT"]:
if i in return_value.keys():
return_value.pop(i)
return return_value
def is_valid_url(text: str) -> bool: def is_valid_url(text: str) -> bool:
try: try:
result = urlparse(text) result = urlparse(text)

View File

@ -83,6 +83,8 @@ class Doctors(Base):
__tablename__ = "doctors" __tablename__ = "doctors"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
code: Mapped[str] = mapped_column(unique=True, nullable=True)
time_zone: Mapped[Optional[str]] = mapped_column(nullable=True)
telegram_id: Mapped[int] = mapped_column(unique=True, nullable=False) telegram_id: Mapped[int] = mapped_column(unique=True, nullable=False)
name: Mapped[str] = mapped_column(nullable=False) name: Mapped[str] = mapped_column(nullable=False)
available_formats: Mapped[Optional[List[str]] available_formats: Mapped[Optional[List[str]]

View File

@ -5,6 +5,7 @@ from telegram.ext import (
CommandHandler, CommandHandler,
) )
from core.logging import logger
from docbot.services.doctors_service import get_doctors_names from docbot.services.doctors_service import get_doctors_names
from docbot.services.admins_service import get_admin_info from docbot.services.admins_service import get_admin_info
@ -22,9 +23,14 @@ async def get_doctors(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int
return ConversationHandler.END return ConversationHandler.END
doctors = await get_doctors_names() doctors = await get_doctors_names()
doctors_table = ""
for row in doctors:
logger.info(f"{row.Doctors.name} {row.VerificationRequests.code}")
doctors_table += f"\n- {row.Doctors.name} (код врача: {row.Doctors.code}) (код верификации: {row.VerificationRequests.code})"
if doctors: if doctors:
await update.message.reply_text( await update.message.reply_text(
f"📝 Список активных врачей. \n {doctors}", f"📝 Список активных врачей. \n {doctors_table}",
parse_mode="Markdown" parse_mode="Markdown"
) )
return ConversationHandler.END return ConversationHandler.END

View File

@ -1,4 +1,4 @@
from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove from telegram import Update, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardButton
from telegram.ext import ( from telegram.ext import (
ContextTypes, ContextTypes,
ConversationHandler, ConversationHandler,
@ -11,7 +11,8 @@ from docbot.handlers.utils.cancel_handler import get_cancel_handler
from docbot.services.referral_service import validate_referral_code from docbot.services.referral_service import validate_referral_code
from docbot.services.doctors_service import create_doctor from docbot.services.doctors_service import create_doctor
from core.enums.dialog_helpers import ConfirmationMessage, Acknowledgement from core.enums.dialog_helpers import ConfirmationMessage, Acknowledgement
from core.utils import UUID_code_generator from core.utils import UUID_code_generator, get_timezones
from core.logging import logger
ASK_REFERRAL_CODE = 1 ASK_REFERRAL_CODE = 1
SEND_ACKNOWLEDGEMENT_INFO = 2 SEND_ACKNOWLEDGEMENT_INFO = 2
@ -19,6 +20,8 @@ ASK_NAME = 3
ASK_SPECIALITY = 4 ASK_SPECIALITY = 4
SEND_DIPLOMA_ACK_INFO = 5 SEND_DIPLOMA_ACK_INFO = 5
SEND_CONSULTATION_TYPE_ACK_INFO = 6 SEND_CONSULTATION_TYPE_ACK_INFO = 6
ASK_TIMEZONE = 7
ASK_CONTINENT = 8
SEND_ME_REFERRAL_CODE_TEXT = ( SEND_ME_REFERRAL_CODE_TEXT = (
"✌️ Пожалуйста, пришлите реферальный код, чтобы начать процесс регистрации." "✌️ Пожалуйста, пришлите реферальный код, чтобы начать процесс регистрации."
@ -34,6 +37,9 @@ SEND_ME_YOUR_SPECIALITY_TEXT = (
"📝 Пожалуйста, введите вашу специальность/специальности, в соответствии с которой планируете проводить консультации.\n" "📝 Пожалуйста, введите вашу специальность/специальности, в соответствии с которой планируете проводить консультации.\n"
"Например: терапевт, кардиолог, невролог и т.д." "Например: терапевт, кардиолог, невролог и т.д."
) )
SELECT_YOUR_TIMEZONE_TEXT = (
"📝 Пожалуйста, выбирете временную зону, в соответствии с которой планируете проводить консультации.\n"
)
WAIT_FOR_ACTIVATION_TEXT = ( WAIT_FOR_ACTIVATION_TEXT = (
"📝 Заявка принята, направьте диплом и аккредитацию с указанием кода верификации {0} в теме письма на адрес электронной почты: docbot@docbot.ru\n" "📝 Заявка принята, направьте диплом и аккредитацию с указанием кода верификации {0} в теме письма на адрес электронной почты: docbot@docbot.ru\n"
"В этом шаге нужно встроить подсказку: в каком формате направлять диплом, как скачать с госуслуг\n" "В этом шаге нужно встроить подсказку: в каком формате направлять диплом, как скачать с госуслуг\n"
@ -101,6 +107,43 @@ async def receive_doctor_name(update: Update, context: ContextTypes.DEFAULT_TYPE
doctor_name = update.message.text.strip() doctor_name = update.message.text.strip()
context.user_data["doctor_name"] = doctor_name context.user_data["doctor_name"] = doctor_name
keyboard = []
for continent in sorted(get_timezones().keys()):
keyboard.append([InlineKeyboardButton(continent)])
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)
await update.message.reply_text(
SELECT_YOUR_TIMEZONE_TEXT,
parse_mode="Markdown",
reply_markup=reply_markup,
)
return ASK_CONTINENT
async def receive_continent(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
keyboard = []
context.user_data["selected_continent"] = selected_continent = update.message.text
for continent in sorted(get_timezones()[selected_continent]):
keyboard.append([InlineKeyboardButton(continent)])
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True)
await update.message.reply_text(
SELECT_YOUR_TIMEZONE_TEXT,
parse_mode="Markdown",
reply_markup=reply_markup,
)
return ASK_TIMEZONE
async def receive_time_zone(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
context.user_data["time_zone"] = context.user_data["selected_continent"] + "/" + update.message.text
logger.info(f"User {context.user_data['doctor_telegram_id']} selected timezone {context.user_data['time_zone']}")
await update.message.reply_text( await update.message.reply_text(
SEND_ME_YOUR_SPECIALITY_TEXT, SEND_ME_YOUR_SPECIALITY_TEXT,
parse_mode="Markdown" parse_mode="Markdown"
@ -168,11 +211,15 @@ async def receive_doctor_consultation_packages_acknowledgement_status(update: Up
reply_markup=ReplyKeyboardRemove() reply_markup=ReplyKeyboardRemove()
) )
doc_code = UUID_code_generator(code_length=5)
await create_doctor( await create_doctor(
context.user_data["ref_obj"], context.user_data["ref_obj"],
context.user_data["doctor_telegram_id"], context.user_data["doctor_telegram_id"],
context.user_data["doctor_name"], context.user_data["doctor_name"],
context.user_data["verification_request_code"] context.user_data["verification_request_code"],
code=doc_code,
time_zone=context.user_data["time_zone"]
) )
return ConversationHandler.END return ConversationHandler.END
@ -198,6 +245,14 @@ def get_register_doctor_first_stage_handler() -> ConversationHandler:
MessageHandler(filters.TEXT & ~filters.COMMAND, MessageHandler(filters.TEXT & ~filters.COMMAND,
receive_doctor_name) receive_doctor_name)
], ],
ASK_TIMEZONE: [
MessageHandler(filters.TEXT & ~filters.COMMAND,
receive_time_zone)
],
ASK_CONTINENT: [
MessageHandler(filters.TEXT & ~filters.COMMAND,
receive_continent)
],
ASK_SPECIALITY: [ ASK_SPECIALITY: [
MessageHandler(filters.TEXT & ~filters.COMMAND, MessageHandler(filters.TEXT & ~filters.COMMAND,
receive_doctor_speciality) receive_doctor_speciality)

View File

@ -17,6 +17,7 @@ from docbot.services.patients_service import (
create_patient, update_patient_phone, get_patient_by_telegram_id, create_patient, update_patient_phone, get_patient_by_telegram_id,
is_user_has_phone is_user_has_phone
) )
from docbot.services.doctors_service import get_doctors_payment_link
from docbot.services.session_service import create_session from docbot.services.session_service import create_session
from core.logging import logger from core.logging import logger
@ -283,7 +284,8 @@ async def pay_consultation(update: Update, context: ContextTypes.DEFAULT_TYPE) -
patient=patient patient=patient
) )
link = f"https://example.com/consultation/{user_id}" link = await get_doctors_payment_link(context.user_data['doctor_number'])
await update.callback_query.answer() await update.callback_query.answer()
await update.callback_query.message.reply_text( await update.callback_query.message.reply_text(
f"Чтобы оплатить консультацию перейдите по ссылке {link}.", f"Чтобы оплатить консультацию перейдите по ссылке {link}.",

View File

@ -24,6 +24,7 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No
"/start Запустить бота\n" "/start Запустить бота\n"
"/genref Сгенерировать новый реферальный код для врача\n" "/genref Сгенерировать новый реферальный код для врача\n"
"/doctors Посмотреть список зарегистрированных врачей\n" "/doctors Посмотреть список зарегистрированных врачей\n"
"/verify Верифицировать врача по реферальному коду\n"
"/help Показать это меню\n" "/help Показать это меню\n"
) )
# (При необходимости в будущем можно добавить другие admin-команды) # (При необходимости в будущем можно добавить другие admin-команды)

View File

@ -24,10 +24,28 @@ async def get_doctor(telegram_id: int) -> Doctors | None:
async def get_doctors_names() -> Doctors | None: async def get_doctors_names() -> Doctors | None:
async with AsyncSessionLocal() as session: async with AsyncSessionLocal() as session:
result = await session.execute( result = await session.execute(
select(Doctors.name) select(Doctors, VerificationRequests)
.join(VerificationRequests, Doctors.id == VerificationRequests.doctor_id)
.where(Doctors.is_active) .where(Doctors.is_active)
) )
return result.all() return result.fetchall()
async def get_doctors_payment_link(doctor_code: str) -> str | None:
async with AsyncSessionLocal() as session:
result = await session.execute(
select(PaymentMethod.method)
.join(Doctors, PaymentMethod.doctor_id == Doctors.id)
.where(
Doctors.code == doctor_code,
PaymentMethod.is_active.is_(True),
PaymentMethod.is_primary.is_(True)
)
)
payment_link = result.scalar_one_or_none()
if payment_link:
return payment_link
return None
async def is_there_primary_payment_method() -> PaymentMethod | None: async def is_there_primary_payment_method() -> PaymentMethod | None:
@ -67,7 +85,7 @@ async def add_doctor(telegram_id: int, name: str, available_formats: Consultatio
)) ))
async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str, verification_code: str) -> Doctors: async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str, verification_code: str, code: str, time_zone: str) -> Doctors:
""" """
Помечает referral_obj как использованный, создаёт запись в таблице Doctors Помечает referral_obj как использованный, создаёт запись в таблице Doctors
с привязкой к telegram_id и возвращает объект Doctor. с привязкой к telegram_id и возвращает объект Doctor.
@ -84,7 +102,9 @@ async def create_doctor(referral_obj: ReferralCode, telegram_id: int, name: str,
name=name, name=name,
is_active=False, is_active=False,
created_at=datetime.utcnow(), created_at=datetime.utcnow(),
referral=referral_obj referral=referral_obj,
code=code,
time_zone=time_zone
) )
verification_request = VerificationRequests() verification_request = VerificationRequests()