You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

564 lines
25 KiB

import requests
import aiohttp
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.utils import executor
import re
from aiogram.types import ReplyKeyboardMarkup, KeyboardButton
import asyncio
from aiogram.dispatcher import FSMContext
from aiogram import types
from aiogram.dispatcher.filters import Command
from datetime import datetime, timedelta
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import re
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo
import logging
from aiogram.types import ReplyKeyboardRemove
TOKEN = '6476840022:AAFlOlYHCH4UKbGlOfILw8xcIUG0AD354X8'
bot = Bot(token=TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)
# Кнопка для перезапуска
restart_button = ReplyKeyboardMarkup(resize_keyboard=True)
restart_button.add(KeyboardButton("Перезапустить бота"))
MAX_ATTEMPTS = 3 # Максимальное количество попыток ввода кода
TIMEOUT = 30 # Время в секундах
# Словарь для хранения данных о пользователях
user_data = {}
last_bot_message_id = {}
# Авторизация и доступ к таблице
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
credentials = ServiceAccountCredentials.from_json_keyfile_name("botforclinic-436512-0c117dd103a8.json", scope)
client = gspread.authorize(credentials)
sheet = client.open("Пациенты клиники").sheet1 # Открытие таблицы, используйте название вашей таблицы
# Словарь для хранения данных о сообщениях пользователей
user_messages = {}
# История состояний для каждого пользователя
user_states = {}
class Form(StatesGroup):
telegram_id = State()
existing_patient = State()
phone_number = State()
fio = State()
birthday = State()
verification_code = State()
confirm_fio = State()
confirm_phone_number = State()
from aiogram import types
# Настройка логирования
logging.basicConfig(level=logging.INFO)
# Главное меню
main_menu = InlineKeyboardMarkup(row_width=2).add(
InlineKeyboardButton("📝 Запись", callback_data="menu_record"),
InlineKeyboardButton("🧰 Услуги", callback_data="menu_services"),
InlineKeyboardButton(" Специалисты", callback_data="menu_specialists"),
InlineKeyboardButton("🌍 Контакты", callback_data="menu_contacts"),
InlineKeyboardButton("📞 Связь", callback_data="menu_contact")
)
# Подменю "Запись"
record_menu = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Запись на приём", callback_data="record_appointment"),
InlineKeyboardButton("Запись на исследование", callback_data="record_research"),
InlineKeyboardButton("Запись на терапию", callback_data="record_therapy"),
InlineKeyboardButton("Назад", callback_data="main_menu")
)
# Меню исследований
research_menu = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("УЗИ", callback_data="research_uzi"),
InlineKeyboardButton("КТ", callback_data="research_kt"),
InlineKeyboardButton("ЭКГ", callback_data="research_ekg"),
InlineKeyboardButton("КСП (скарификационные пробы)", callback_data="research_ksp"),
InlineKeyboardButton("Назад", callback_data="menu_record"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
# Меню терапии
therapy_menu = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Слухопротезирование", callback_data="therapy_hearing"),
InlineKeyboardButton("АСИТ-терапия", callback_data="therapy_asit"),
InlineKeyboardButton("ФДТ (фотодинамическая терапия)", callback_data="therapy_fdt"),
InlineKeyboardButton("Галотерапия (соляная комната)", callback_data="therapy_halo"),
InlineKeyboardButton("Назад", callback_data="menu_record"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
# Обработка нажатий на инлайн-кнопки
@dp.callback_query_handler(lambda c: c.data.startswith("menu_") or c.data == "main_menu")
async def handle_main_menu(call: types.CallbackQuery):
if call.data == "main_menu":
await call.message.edit_text("Главное меню:", reply_markup=main_menu)
elif call.data == "menu_record":
await call.message.edit_text("Выберите тип записи:", reply_markup=record_menu)
# Обработка нажатия на "Запись на исследование"
@dp.callback_query_handler(lambda c: c.data == "record_research")
async def handle_research_menu(call: types.CallbackQuery):
await call.message.edit_text("Выберите тип исследования:", reply_markup=research_menu)
# Обработка нажатия на "Запись на терапию"
@dp.callback_query_handler(lambda c: c.data == "record_therapy")
async def handle_therapy_menu(call: types.CallbackQuery):
await call.message.edit_text("Выберите тип терапии:", reply_markup=therapy_menu)
# Обработка нажатий на инлайн-кнопки
@dp.callback_query_handler(lambda c: c.data.startswith("menu_") or c.data == "main_menu")
async def handle_main_menu(call: types.CallbackQuery):
if call.data == "main_menu":
await call.message.edit_text("Главное меню:", reply_markup=main_menu)
elif call.data == "menu_record":
await call.message.edit_text("Выберите тип записи:", reply_markup=record_menu)
# Обработка нажатия на "Запись на приём"
@dp.callback_query_handler(lambda c: c.data == "record_appointment")
async def show_specialties(call: types.CallbackQuery):
spec_url = "http://46.146.229.242:1980/AppZaprSpec"
try:
response = requests.post(spec_url)
logging.info(f"Ответ от сервера специальностей: {response.text}")
if response.status_code == 200:
specialties = response.json()
specialties_menu = InlineKeyboardMarkup(row_width=1)
for spec in specialties:
if 'MSP_ID' in spec and 'MSP_NAME' in spec:
specialties_menu.add(
InlineKeyboardButton(
spec['MSP_NAME'], callback_data=f"spec_{spec['MSP_ID']}"
)
)
specialties_menu.add(
InlineKeyboardButton("Назад", callback_data="menu_record"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
await call.message.edit_text("Выберите специальность:", reply_markup=specialties_menu)
else:
await call.message.answer(f"Ошибка сервера. Код ответа: {response.status_code}")
except Exception as e:
logging.error(f"Ошибка получения специальностей: {e}")
await call.message.answer("Произошла ошибка при получении специальностей.")
# Обработка выбора специальности
@dp.callback_query_handler(lambda c: c.data.startswith("spec_"))
async def show_doctors(call: types.CallbackQuery):
spec_id = call.data.split('_')[1]
doc_url = "http://46.146.229.242:1980/AppZaprSpecDoc"
try:
doc_response = requests.post(doc_url, json={"spec_id": int(spec_id)})
logging.info(f"Ответ от сервера врачей: {doc_response.text}")
if doc_response.status_code == 200:
doctors = doc_response.json()
if not doctors:
await call.message.answer("Врачи по этой специальности не найдены.")
return
doctors_menu = InlineKeyboardMarkup(row_width=1)
for doc in doctors:
if 'DOC_FIO' in doc and 'DOC_ID' in doc:
doctors_menu.add(
InlineKeyboardButton(
f"{doc['DOC_FIO']}", callback_data=f"doc_{doc['DOC_ID']}"
)
)
doctors_menu.add(
InlineKeyboardButton("Назад", callback_data="record_appointment"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
await call.message.edit_text("Выберите врача:", reply_markup=doctors_menu)
else:
await call.message.answer(f"Ошибка сервера. Код ответа: {doc_response.status_code}")
except Exception as e:
logging.error(f"Ошибка получения врачей: {e}")
await call.message.answer("Произошла ошибка при получении списка врачей.")
@dp.message_handler(commands=['test'])
async def handle_help(message: types.Message):
user_id = message.from_user.id
await bot.send_message(user_id,"тестирование кнопок меню", reply_markup=main_menu)
@dp.message_handler(commands=['help'])
async def handle_help(message: types.Message):
#await save_message_data(message)
user_id = message.from_user.id
help_message = "Список доступных команд:\n"
help_message += "/start - начать взаимодействие с ботом\n"
help_message += "/registration - регистрация\n"
help_message += "/help - показать список команд и их описания\n"
sent_message = await bot.send_message(user_id, help_message)
last_bot_message_id[user_id] = sent_message.message_id
@dp.message_handler(Command("start"), state="*")
async def handle_start(message: types.Message, state: FSMContext):
# Завершаем текущее состояние и очищаем данные пользователя
await state.finish() # Завершает текущее состояние
await state.storage.reset_data(user=message.from_user.id) # Удаляет все данные пользователя в FSM хранилище
# Отправляем фото
with open("s-blob-v1-IMAGE-tdNCrEv8Ldo.png", "rb") as photo:
await bot.send_photo(
chat_id=message.from_user.id,
photo=photo,
caption=("Добро пожаловать!\nЯ чат-бот Клиники и Ваш надежный виртуальный помощник.\nЧтобы узнать, что я могу для Вас сделать, просто введите /help\nДля начала работы нажмите кнопку /registration внизу."),
reply_markup=generate_markup_registration() # Кнопка для продолжения
)
@dp.message_handler(commands=['registration'], chat_type=types.ChatType.PRIVATE)
async def handle_registration(message: types.Message):
user_id = message.from_user.id
sent_message = await bot.send_message(user_id, "Вы уже являетесь пациентом нашей Клиники?\nЕсли Вы хотите начать общение с ботом заново, нажмите /start.",
reply_markup=yes_no_markup())
last_bot_message_id[user_id] = sent_message.message_id
await Form.existing_patient.set()
def generate_markup_telegram_id():
markup = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
item3 = types.KeyboardButton("Отправить Telegram ID")
markup.add(item3)
return markup
def generate_markup_registration():
markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
item_registration = types.KeyboardButton("/registration")
markup.add(item_registration)
return markup
@dp.message_handler(state=Form.existing_patient)
async def handle_existing_patient(message: types.Message, state: FSMContext):
user_id = message.from_user.id
form_url = f"https://tgbotpolimed.pirogov.ai/?user_id={user_id}"
if message.text.lower() == "да":
# Удаляем кнопки и отправляем ссылку на форму
await message.answer(
"В соответствии с Федеральным законом № 152-ФЗ «О персональных данных», для идентификации Вас как пациента нашей Клиники, просим заполнить форму, нажав на кнопку ниже.",
reply_markup=ReplyKeyboardRemove()
)
await message.answer(
"Заполнить форму:",
reply_markup=InlineKeyboardMarkup().add(
InlineKeyboardButton(
text="Заполнить форму",
web_app=WebAppInfo(url=form_url)
)
)
)
elif message.text.lower() == "нет":
await bot.send_message(
user_id,
"Полный доступ к функциям сервиса доступен только пациентам нашей Клиники.\n"
"Вы можете связаться с нами по тел.+7 (342) 207-03-03 или посетить Клинику лично по адресам:\n"
"г. Пермь, ул. Клары Цеткин, д. 9; ул. Газеты Звезда, д. 31- а.\n\n"
"Будьте здоровы!",
reply_markup=ReplyKeyboardRemove()
)
await state.finish()
else:
await bot.send_message(
user_id,
"Пожалуйста, выберите 'Да' или 'Нет' с помощью кнопок.",
reply_markup=ReplyKeyboardRemove()
)
@dp.message_handler(state=Form.verification_code)
async def handle_verification_code(message: types.Message, state: FSMContext):
user_id = message.from_user.id
code_entered = message.text.strip()
expected_code = user_data.get(user_id, {}).get("verification_code")
if expected_code and str(code_entered) == str(expected_code):
await bot.send_message(user_id, "Верификация прошла успешно! Пожалуйста, подождите...")
# Поиск данных пациента по Telegram ID
patient_records = find_patients_by_id(user_id)
if len(patient_records) == 0:
await bot.send_message(user_id, "Пациенты с данным Telegram ID не найдены в базе данных.")
await state.finish()
elif len(patient_records) == 1:
# Если только одна запись, сразу берём данные
await process_patient_data(user_id, patient_records[0], state)
else:
# Если несколько записей, предлагаем выбрать пациента
await prompt_patient_selection(user_id, patient_records)
else:
await bot.send_message(user_id, "Неверный код. Попробуйте снова.")
def find_patients_by_id(telegram_id):
records = sheet.get_all_records() # Получаем все строки таблицы
patients = []
for record in records:
if str(record["Telegram ID"]) == str(telegram_id):
patients.append(record)
return patients
async def prompt_patient_selection(user_id, patient_records):
# Создаем кнопки с ФИО пациентов
markup = ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
for record in patient_records:
markup.add(KeyboardButton(record["ФИО"]))
# Сохраняем данные пациентов временно
user_data[user_id]["patients"] = patient_records
await bot.send_message(
user_id,
"Найдено несколько записей с вашим Telegram ID. Пожалуйста, выберите пациента:",
reply_markup=markup,
)
await Form.fio.set()
@dp.message_handler(state=Form.fio)
async def handle_patient_selection(message: types.Message, state: FSMContext):
user_id = message.from_user.id
selected_fio = message.text.strip()
# Поиск выбранного пациента
patients = user_data[user_id].get("patients", [])
selected_patient = next((p for p in patients if p["ФИО"] == selected_fio), None)
if selected_patient:
await process_patient_data(user_id, selected_patient, state)
else:
await bot.send_message(user_id, "Ошибка: выбранный пациент не найден. Попробуйте ещё раз.")
async def process_patient_data(user_id, patient_record, state: FSMContext):
# Форматирование данных
fio_parts = patient_record["ФИО"].split()[:3] # Берем первые три слова из ФИО
formatted_phone = find_patient_by_id(user_id) # Используем функцию для получения номера телефона
if formatted_phone:
# Сохранение в словарь
user_data[user_id] = {
"telegram_id": user_id,
"first_name": fio_parts[0],
"middle_name": fio_parts[1] if len(fio_parts) > 1 else "",
"last_name": fio_parts[2] if len(fio_parts) > 2 else "",
"phone_number": formatted_phone,
"birthday": patient_record["Дата рождения"],
}
# Отправка на сервер
result = send_registration_request(user_data[user_id])
if result == "success":
await bot.send_message(user_id, "Регистрация успешно завершена!")
else:
await bot.send_message(user_id, "Произошла ошибка при регистрации. Пожалуйста, попробуйте позже.")
else:
await bot.send_message(user_id, "Не удалось найти номер телефона для вашего аккаунта.")
await state.finish()
def send_verification_call(user_id, phone_number):
url = "https://sms.ru/code/call"
data = {
"phone": phone_number,
"api_id": "2ED72E61-76C8-5637-3587-2792D47B698C"
}
response = requests.post(url, data=data)
json_data = response.json()
verification_code = None
# Проверяем статус запроса
if json_data and json_data.get("status") == "OK":
verification_code = json_data.get("code") # Получаем код
if not verification_code:
print("Ошибка: Код отсутствует в ответе API.")
return None
# Сохраняем код для пользователя
if user_id not in user_data:
user_data[user_id] = {}
user_data[user_id]["verification_code"] = verification_code
# Логируем код для отладки
print(f"Код верификации, отправленный пользователю {user_id}: {verification_code}")
else:
print("Звонок не может быть выполнен.")
print("Текст ошибки:", json_data.get("status_text"))
return verification_code
async def save_message_data(message: types.Message, reply_to_message_id=None):
user_id = message.from_user.id
reply_to_message_id = last_bot_message_id.get(user_id, None)
user_messages[message.message_id] = {
"message_id": message.message_id,
"text": message.text,
"date": message.date.isoformat(), # Исправлено
"user_id": message.from_user.id,
"reply_to_message_id": reply_to_message_id
}
print(f"Сообщение пользователя {user_id} сохранено: {user_messages[message.message_id]}")
await send_message_to_server(user_messages[message.message_id])
def send_registration_request(user_data):
HEADER = {
"Content-Type": "application/json"
}
data = {
"telegram_id": user_data.get("telegram_id"),
"first_name": user_data["first_name"],
"second_name": user_data["middle_name"],
"last_name": user_data["last_name"],
"mobile_phone": user_data["phone_number"],
"birthday": user_data["birthday"]
}
print("Отправляемые данные на сервер:", data)
response = requests.post("http://46.146.229.242:1980/AppFindPac", headers=HEADER, json=data)
if response.status_code == 200:
result = response.json()
print("Результат JSON:", result)
return result.get('result')
else:
print(f"Ошибка при отправке данных. Статус-код: {response.status_code}")
return "error"
async def send_message_to_server(message_data):
url = "http://46.146.229.242:1980/AppSaveMessage"
headers = {"Content-Type": "application/json"}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=message_data) as response:
if response.status == 200:
print(f"Сообщение {message_data['message_id']} успешно отправлено на сервер.")
else:
print(f"Ошибка при отправке сообщения {message_data['message_id']} на сервер. Статус-код: {response.status}")
# Функция для выполнения POST-запроса и получения списка врачей
def get_doctors():
url = "http://46.146.229.242:1980/AppZaprSpecDoc"
# Данные для отправки в запросе
data = {"spec_id": 1}
# Выполняем POST-запрос
response = requests.post(url, json=data)
# Проверяем статус ответа
if response.status_code == 200:
# Если запрос успешен, возвращаем данные
return response.json()
else:
# Если запрос не удался, выводим ошибку
print(f"Ошибка: {response.status_code}")
return []
def yes_no_markup():
markup = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
item_no = types.KeyboardButton("Нет")
item_yes = types.KeyboardButton("Да")
markup.add(item_yes, item_no)
return markup
def markup2():
markup = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
item_good = types.KeyboardButton("Всё верно")
item_no_good = types.KeyboardButton("Указать ФИО ещё раз")
markup.add(item_good, item_no_good)
return markup
def generate_start_markup():
markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
start_button = types.KeyboardButton("/start")
markup.add(start_button)
return markup
def find_patient_by_id(telegram_id):
records = sheet.get_all_records() # Получаем все строки таблицы
for record in records:
if str(record["Telegram ID"]) == str(telegram_id): # Сравнение с Telegram ID
phone_raw = record["Номер телефона"]
# Удаляем все нецифровые символы
phone_formatted = re.sub(r"\D", "", phone_raw)
# Проверка на правильную длину номера и корректировка, если необходимо
if len(phone_formatted) == 11:
if phone_formatted.startswith("8"): # Если номер начинается с 8, заменяем на 7
phone_formatted = "7" + phone_formatted[1:]
return phone_formatted
else:
return None # Возвращаем None, если номер неправильной длины
return None
def generate_markup_menu():
# Создаем клавиши
buttons = [
KeyboardButton("📝 Запись"),
KeyboardButton("🧰 Услуги"),
KeyboardButton(" Специалисты"),
KeyboardButton("🌍 Контакты"),
KeyboardButton("📞 Связь"),
]
# Создаем и настраиваем клавиатуру
markup = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=False)
# Добавляем кнопки в столбик (по одной в строке)
markup.add(*buttons)
return markup
@dp.message_handler(content_types=types.ContentType.TEXT)
async def handle_message(message: types.Message):
await save_message_data(message)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)