From 50b993ead2a0a185de31b989d885e7427ef059f2 Mon Sep 17 00:00:00 2001 From: Ilya_Chis Date: Thu, 26 Dec 2024 01:20:00 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D0=B1=D0=BE=D1=82=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D0=B8=D0=BC=D0=B5=D0=B4=D0=B0=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BotPM.py | 450 +++++++++++++++++++++++++------------------------------ 1 file changed, 207 insertions(+), 243 deletions(-) diff --git a/BotPM.py b/BotPM.py index 0d545be..a8d6e78 100644 --- a/BotPM.py +++ b/BotPM.py @@ -64,13 +64,21 @@ class Form(StatesGroup): - +# Создаем состояния +class QuestionForm(StatesGroup): + waiting_for_question = State() from aiogram import types + + + + + + # Настройка логирования logging.basicConfig(level=logging.INFO) @@ -82,33 +90,43 @@ main_menu = InlineKeyboardMarkup(row_width=2).add( # Подменю "контакты" contacts_menu = InlineKeyboardMarkup(row_width=1).add( - InlineKeyboardButton("Назад", callback_data="main_menu") + 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"), + 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") + InlineKeyboardButton("✍🏼 Написать сообщение", callback_data="message_admin"), + InlineKeyboardButton("▶️ Назад", callback_data="menu_contact"), + InlineKeyboardButton("▶️ Главное меню", callback_data="main_menu") +) + +message_ad = InlineKeyboardMarkup(row_width=1).add( + InlineKeyboardButton("▶️ Назад", callback_data="contact_administrator"), + 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") +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") + InlineKeyboardButton("▶️ Назад", callback_data="order_call"), + InlineKeyboardButton("▶️ Главное меню", callback_data="main_menu") +) + +whatsapp = InlineKeyboardMarkup(row_width=1).add( + InlineKeyboardButton("▶️ Главное меню", callback_data="main_menu") ) @@ -120,33 +138,30 @@ async def handle_main_menu(call: types.CallbackQuery): " В ближайшее время у меня появятся новые функции и возможности." " А пока Вы можете ознакомиться с текущим меню.", 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) + await call.message.edit_text("Клиники им. проф. Е.Н.Оленевой\n\n" + "Клиника УХО•ГОРЛО•НОС\n" + "Записаться на приём, задать вопрос:\n" + "📞 +7 (342) 207-03-03\n\n" + "Больше информации о клинике и врачах на нашем 🔗САЙТЕ\n" + "📱г. Пермь, ул. К. Цеткин, 9\n" + "(ост. Строительный факультет)\n" + "Построить маршрут\n\n" + "--------------\n\n" + "Клиника лечения КАШЛЯ•АЛЛЕРГИИ\n" + "Записаться на приём, задать вопрос:\n" + "📞 +7 (342) 200-02-03\n\n" + "Больше информации о клинике и врачах на нашем 🔗САЙТЕ\n\n" + "📱г. Пермь, ул. Пермь, ул. Г. Звезда, 31А\n" + "(ост. Г. Звезда)\n" + "Построить маршрут\n\n" + "--------------\n\n" + "Центр ДИАГНОСТИКИ И РЕАБИЛИТАЦИИ \n" + "📞 +7 (342) 287-16-94\n\n" + "Больше информации о клинике и врачах на нашем 🔗САЙТЕ\n\n" + "📱 г. Пермь, ул. Пермь, ул. Г. Звезда, 31А\n" + " (ост. Г. Звезда)\n" + "Построить маршрут\n\n" + "Присоединяйтесь к нам в Телеграм", reply_markup=contacts_menu, parse_mode="HTML") elif call.data == "menu_contact": await call.message.edit_text("Выберите способ связи", reply_markup=contact_menu) @@ -154,7 +169,7 @@ async def handle_main_menu(call: types.CallbackQuery): # Обработка нажатия на "Запись на исследование" @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) + await call.message.edit_text("💬 Для отправки сообщения администратору Клиники нажмите кнопку ниже.", reply_markup=administrator_contact) # Обработка нажатия на "Запись на исследование" @dp.callback_query_handler(lambda c: c.data == "order_call") @@ -191,96 +206,152 @@ async def handle_ad_call_menu(call: types.CallbackQuery): 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" +# Обработчик кнопки "message_admin" +@dp.callback_query_handler(lambda c: c.data == "message_admin") +async def handle_ad_message(call: types.CallbackQuery, state: FSMContext): + user_id = str(call.from_user.id) # Преобразуем в строку для сравнения try: - doc_response = requests.post(doc_url, json={"spec_id": int(spec_id)}) - logging.info(f"Ответ от сервера врачей: {doc_response.text}") + # Получение всех записей из таблицы + records = sheet.get_all_records() - if doc_response.status_code == 200: - doctors = doc_response.json() - if not doctors: - await call.message.answer("Врачи по этой специальности не найдены.") - return + # Поиск пользователя по Telegram ID + user_data = next((entry for entry in records if str(entry['Telegram ID']) == user_id), None) - doctors_menu = InlineKeyboardMarkup(row_width=1) + if not user_data: + await call.message.answer("Ваши данные не найдены.", reply_markup=message_ad) + return - 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']}" - ) - ) + # Переход в состояние ожидания вопроса + prompt_message = await call.message.edit_text( + "Пожалуйста, отправьте ваш вопрос администратору одним сообщением.", + reply_markup=message_ad + ) - doctors_menu.add( - InlineKeyboardButton("Назад", callback_data="record_appointment"), + # Сохраняем ID сообщения + async with state.proxy() as data: + data["prompt_message_id"] = prompt_message.message_id + + await QuestionForm.waiting_for_question.set() # Устанавливаем состояние ожидания + + except Exception as e: + logging.error(f"Ошибка при доступе к таблице: {e}") + await call.message.edit_text("Произошла ошибка. Попробуйте снова позже.", reply_markup=message_ad) + +# Обработчик ввода вопроса +@dp.message_handler(state=QuestionForm.waiting_for_question, content_types=types.ContentTypes.TEXT) +async def handle_user_question(message: types.Message, state: FSMContext): + user_id = str(message.from_user.id) + user_question = message.text + + try: + # Удаление предыдущего сообщения + async with state.proxy() as data: + if "prompt_message_id" in data: + await bot.delete_message(chat_id=message.chat.id, message_id=data["prompt_message_id"]) + + # Здесь можно отправить вопрос на сервер или обработать его + await send_message_server(user_id, user_question) + + # Ответ пользователю + await message.answer( + "Ваше сообщение успешно отправлено! Администратор клиники ответит Вам в ближайшее время. Спасибо за Ваше обращение!", + reply_markup=message_ad + ) + except Exception as e: + logging.error(f"Ошибка при обработке вопроса: {e}") + await message.answer( + "Произошла ошибка при отправке вопроса. Попробуйте позже.", + reply_markup=message_ad + ) + + # Завершаем состояние + await state.finish() + + + + +# Хранилище для уже обработанных пользователей +processed_users = set() + +# Функция проверки таблицы +async def check_verification_status(): + # while True: + try: + # Чтение всех строк таблицы + records = sheet.get_all_records() + for row in records: + fio = row.get("ФИО") + phone = row.get("Номер телефона") + birth_date = row.get("Дата рождения") + telegram_id = row.get("Telegram ID") + timestamp = row.get("Время заполнения") + verification_status = row.get("Верификация") + + # Проверяем, если "Верификация" = "Пройдена" и сообщение ещё не отправлено + if ( + verification_status + and verification_status.strip().lower() == "пройдена" + and telegram_id not in processed_users + ): + await send_success_message(telegram_id) + processed_users.add(telegram_id) # Добавляем пользователя в обработанные + return None + except Exception as e: + print(f"Ошибка при проверке верификации: {e}") + + # Интервал проверки (например, каждые 60 секунд) + await asyncio.sleep(1) + +# Функция отправки сообщения +async def send_success_message(telegram_id): + try: + await bot.send_message( + chat_id=telegram_id, + text=( + "Ура! Ваша регистрация прошла успешно! 🥳\n\n" + "Теперь все уведомления о записи на приём будут приходить сюда, в чат-бот. " + "Если Вы также получаете рассылку в WhatsApp, то сообщения будут дублироваться.\n\n" + "Для получения рассылки только в чат-бот Telegram, нажмите кнопку ниже." + ), + reply_markup=InlineKeyboardMarkup().add( + InlineKeyboardButton("Не дублировать сообщения в WhatsApp", callback_data="stop_replying"), 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("Произошла ошибка при получении списка врачей.") + print(f"Ошибка при отправке сообщения пользователю {telegram_id}: {e}") + +# Обработка нажатия кнопок +@dp.callback_query_handler(lambda c: c.data == "stop_replying") +async def stop_replying_handler(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=whatsapp) + return + + phone_number = user_data['Номер телефона'] # Измените на название вашего столбца + + # Отправка на сервер + await send_message_server(phone_number, "Отменить рассылку в WhatsApp") + + # Ответ пользователю + await call.message.edit_text("Рассылка в WhatsApp отключена.", reply_markup=whatsapp) + + except Exception as e: + logging.error(f"Ошибка при доступе к таблице: {e}") + await call.message.edit_text("Произошла ошибка. Попробуйте снова позже.", reply_markup=whatsapp) + -@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']) @@ -347,7 +418,7 @@ async def handle_existing_patient(message: types.Message, state: FSMContext): if message.text.lower() == "да": await message.answer("В соответствии с Федеральным законом № 152-ФЗ «О персональных данных», для идентификации Вас как пациента нашей Клиники, просим заполнить форму, нажав на кнопку ниже.",reply_markup=ReplyKeyboardRemove()) - await message.answer("Нажмите кнопку ниже, чтобы ввести персональные данные:", + await message.answer("Ввод персональных данных", reply_markup=InlineKeyboardMarkup().add( InlineKeyboardButton( text="Заполнить форму", @@ -355,14 +426,13 @@ async def handle_existing_patient(message: types.Message, state: FSMContext): ) ) ) + await asyncio.create_task(check_verification_status()) + await state.finish() 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() @@ -373,29 +443,8 @@ async def handle_existing_patient(message: types.Message, state: FSMContext): "Пожалуйста, выберите 'Да' или 'Нет' с помощью кнопок.", reply_markup=ReplyKeyboardRemove() # Удаляем кнопки "Да"/"Нет" ) + await state.finish() -@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() # Получаем все строки таблицы @@ -421,79 +470,6 @@ async def prompt_patient_selection(user_id, patient_records): ) 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 @@ -510,30 +486,7 @@ async def save_message_data(message: types.Message, reply_to_message_id=None): 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" @@ -571,7 +524,7 @@ 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) + markup.add(item_yes, item_no) return markup def markup2(): @@ -629,6 +582,17 @@ def find_patient_by_id(telegram_id): + +def authorize_google(): + # google_json = {} + # for key in config["GOOGLE"].keys(): + # google_json[key] = config["GOOGLE"][key] + scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"] + creds = ServiceAccountCredentials.from_json_keyfile_name("botforclinic-436512-0c117dd103a8.json", scope) + # creds = ServiceAccountCredentials.from_json_keyfile_dict(google_json, scope) + client = gspread.authorize(creds) + return client + @dp.message_handler(content_types=types.ContentType.TEXT) async def handle_message(message: types.Message): await save_message_data(message)