Files
TG_Bot_Polimed/BotPM.py
T
2024-12-19 23:31:53 +05:00

637 lines
30 KiB
Python
Raw 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.
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_contacts"),
InlineKeyboardButton("📞 Связь", callback_data="menu_contact")
)
# Подменю "контакты"
contacts_menu = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Назад", callback_data="main_menu")
)
# Меню связь
contact_menu = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("📣связаться с администратором Клиники", callback_data="contact_administrator"),
InlineKeyboardButton("📥заказать звонок от администратора", callback_data="order_call"),
InlineKeyboardButton("Назад", callback_data="main_menu"),
)
administrator_contact = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Назад", callback_data="menu_contact"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
call_order = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Позвонить мне", callback_data="call_me"),
InlineKeyboardButton("Назад", callback_data="menu_contact"),
InlineKeyboardButton("Главное меню", callback_data="main_menu")
)
return_call_me = InlineKeyboardMarkup(row_width=1).add(
InlineKeyboardButton("Назад", callback_data="order_call"),
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_contacts":
await call.message.edit_text("🌿Клиники им. проф. Е.Н.Оленевой\n\n"
"🏥 Клиника УХО•ГОРЛО•НОС\n\n"
"📬 г. Пермь, ул. К.Цеткин,9\n"
"(ост. Строительный факультет)\n\n"
"☎️+7(342)207-03-03\n"
"🔗Ссылка на сайт:\n"
"https://perm.oclinica.ru/lor\n\n"
"🛞Построить маршрут по ссылке:\n"
"https://tinyurl.com/286a8tv7\n\n"
"************************************\n\n"
"🏥 Клиника КАШЛЯ•АЛЛЕРГИИ\n\n"
"📬 г. Пермь, ул. Г.Звезда,31А\n"
"(ост. Г.Звезда)\n\n"
"☎️+7(342)207-03-03\n"
"🔗Ссылка на сайт:\n"
"https://perm.oclinica.ru/allergo\n\n"
"🛞Построить маршрут по ссылке\n"
"https://tinyurl.com/9a9w5pdu\n\n"
"************************************\n\n"
"🏥 Центр ДИАГНОСТИКИ И РЕАБИЛИТАЦИИ\n\n"
"📬 г. Пермь, ул. Г.Звезда,31А\n"
"(ост. Г.Звезда)\n\n"
"☎️ +7(342) 292-24-45\n"
"🔗Ссылка на сайт:\n"
"https://cdr.oclinica.ru\n\n"
"🛞Построить маршрут по ссылке\n"
"https://tinyurl.com/9a9w5pdu", reply_markup=contacts_menu)
elif call.data == "menu_contact":
await call.message.edit_text("Выберите способ связи", reply_markup=contact_menu)
# Обработка нажатия на "Запись на исследование"
@dp.callback_query_handler(lambda c: c.data == "contact_administrator")
async def handle_call_menu(call: types.CallbackQuery):
await call.message.edit_text("📲Вы можете связаться с нашим администратором по телефону: +7(342) 207-03-03.", reply_markup=administrator_contact)
# Обработка нажатия на "Запись на исследование"
@dp.callback_query_handler(lambda c: c.data == "order_call")
async def handle_admin_call_menu(call: types.CallbackQuery):
await call.message.edit_text("✍🏼 Для получения обратного звонка от администратора Клиники нажмите кнопку ниже.", reply_markup=call_order)
@dp.callback_query_handler(lambda c: c.data == "call_me")
async def handle_ad_call_menu(call: types.CallbackQuery):
user_id = str(call.from_user.id) # Преобразуем в строку для сравнения
try:
# Получение всех записей из таблицы
records = sheet.get_all_records()
# Поиск пользователя по Telegram ID
user_data = next((entry for entry in records if str(entry['Telegram ID']) == user_id), None)
if not user_data:
await call.message.answer("Ваши данные не найдены.", reply_markup=return_call_me)
return
phone_number = user_data['Номер телефона'] # Измените на название вашего столбца
# Отправка на сервер
await send_message_server(phone_number, "Заказан звонок")
# Ответ пользователю
await call.message.edit_text("В ближайшее время с Вами свяжется администратор и проконсультирует по всем вопросам.", reply_markup=return_call_me)
except Exception as e:
logging.error(f"Ошибка при доступе к таблице: {e}")
await call.message.edit_text("Произошла ошибка. Попробуйте снова позже.", reply_markup=return_call_me)
# # Обработка нажатия на "Запись на терапию"
# @dp.callback_query_handler(lambda c: c.data == "order_call")
# 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 not message.text.strip():
await message.answer(
"Пожалуйста, выберите 'Да' или 'Нет' с помощью кнопок.",
reply_markup=ReplyKeyboardRemove()
)
return
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_no, item_yes)
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
async def send_message_server(phone_number, text):
url = "http://46.146.229.242:1980/AppSaveMessage"
headers = {"Content-Type": "application/json"}
message_data = {
"phone_number": phone_number,
"text": text
}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, json=message_data) as response:
if response.status == 200:
print("Сообщение успешно отправлено на сервер.")
print(message_data)
else:
print(f"Ошибка при отправке сообщения на сервер. Статус-код: {response.status}")
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
@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)