Advertisement
Hasli4

TgBotMedical

Apr 4th, 2025
328
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 14.63 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. Полноценная реализация чат-бота для Telegram.
  4. Бот обрабатывает входящие сообщения следующим образом:
  5. 1. Получает сообщение от пользователя.
  6. 2. С помощью NLTK:
  7.   - Токенизирует текст,
  8.   - Удаляет стоп-слова,
  9.   - Оставляет только слова (без знаков препинания и чисел).
  10. 3. Приводит слова к нормальной форме (лемматизация) с помощью spaCy.
  11. 4. Дополнительно для симптомов пытается преобразовать глагол в существительное (например, "тошнить" -> "тошнота") с помощью pymorphy2.
  12. 5. Загружает таблицу "Лекарства.xlsx" с данными
  13.   (столбцы: раздел, подраздел, наименование, Фармакологическое действие, Показания к применению, Побочные эффекты).
  14. 6. Производится поиск по всем столбцам.
  15. 7. Если по столбцу "наименование" находятся совпадения, бот выводит красиво отформатированную справку с эмодзи.
  16. 8. Если препарата не найдено, но совпадения обнаружены в столбцах "Показания к применению" или "Побочных эффектов",
  17.   выводится сообщение с рекомендациями.
  18. 9. Если совпадений нет – выводится сообщение "Информация по запросу не найдена."
  19. 10. Бот также обрабатывает команды /start, /help и "Выход" для завершения работы.
  20. """
  21.  
  22. import os
  23. import re
  24. import telebot
  25. import pandas as pd
  26. import nltk
  27. from nltk.tokenize import word_tokenize
  28. from nltk.corpus import stopwords
  29. import spacy
  30. import pymorphy2
  31.  
  32. # Загружаем необходимые ресурсы NLTK
  33. nltk.download('punkt')
  34. nltk.download('stopwords')
  35.  
  36. # Загружаем spaCy модель для русского языка
  37. nlp = spacy.load("ru_core_news_md")
  38.  
  39. # Инициализируем pymorphy2 для морфологического анализа
  40. morph = pymorphy2.MorphAnalyzer()
  41.  
  42. # Загружаем Excel-файл с лекарствами.
  43. try:
  44.     df = pd.read_excel("Лекарства.xlsx")
  45. except Exception as e:
  46.     print("Ошибка при загрузке файла Excel:", e)
  47.     df = None
  48.  
  49. # Получаем токен из переменной окружения
  50. TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
  51. if TOKEN is None:
  52.     raise ValueError("Переменная окружения TELEGRAM_BOT_TOKEN не установлена!")
  53.  
  54. bot = telebot.TeleBot(TOKEN)
  55.  
  56.  
  57. def process_message(text):
  58.     """
  59.    Обрабатывает входящий текст:
  60.      - Приводит к нижнему регистру.
  61.      - Токенизирует, удаляет стоп-слова, оставляя только буквы.
  62.      - Лемматизирует текст с помощью spaCy.
  63.      - Для каждого токена, если он является глаголом, пытается найти производную существительную форму.
  64.      - Применяет обработку синонимов (например, "насморк" -> "ринит")
  65.      - Обрабатывает симптомы: заменяет "болеть" на "боль".
  66.      - Если встречаются и "боль" и локализационные слова (например, "живот", "рука", "кости"),
  67.        локализационные токены сохраняются отдельно и удаляются из итогового списка для поиска.
  68.    Возвращает кортеж: (список лемм для поиска, список найденных локаций).
  69.  
  70.    Изменения: строки примерно 40-70.
  71.    """
  72.     text = text.lower()
  73.     tokens = word_tokenize(text, language="russian")
  74.     # Фильтруем: оставляем только буквы, исключаем стоп-слова и токены длиной меньше 3 символов
  75.     filtered_tokens = [token for token in tokens
  76.                        if token.isalpha() and token not in stopwords.words("russian") and len(token) > 2]
  77.     doc = nlp(" ".join(filtered_tokens))
  78.     lemmas = []
  79.     for token in doc:
  80.         lemma = token.lemma_
  81.         # Преобразование глаголов в существительные (например, "тошнить" -> "тошнота", "рвать" -> "рвота")
  82.         p = morph.parse(lemma)[0]
  83.         if 'VERB' in p.tag and lemma in ['тошнить', 'рвать']:
  84.             mapping = {'тошнить': 'тошнота', 'рвать': 'рвота'}
  85.             lemma = mapping.get(lemma, lemma)
  86.         # Обработка просторечных выражений (например, "насморк" -> "ринит")
  87.         colloquial_mapping = {
  88.             'насморк': 'ринит'
  89.         }
  90.         if lemma in colloquial_mapping:
  91.             lemma = colloquial_mapping[lemma]
  92.         # Обработка симптомов: замена "болеть" на "боль"
  93.         symptom_mapping = {
  94.             'болеть': 'боль'
  95.         }
  96.         if lemma in symptom_mapping:
  97.             lemma = symptom_mapping[lemma]
  98.         lemmas.append(lemma)
  99.     # Определяем локализационные ключевые слова
  100.     location_candidates = ['живот', 'рука', 'кости']
  101.     locations_found = [token for token in lemmas if token in location_candidates]
  102.     # Если встречаются и "боль" и локализация, удаляем локализационные токены из списка для поиска
  103.     if "боль" in lemmas and locations_found:
  104.         for loc in locations_found:
  105.             while loc in lemmas:
  106.                 lemmas.remove(loc)
  107.     return lemmas, locations_found
  108.  
  109.  
  110. def search_tokens_in_table(tokens, dataframe):
  111.     """
  112.    Ищет совпадения для каждого токена по всем столбцам.
  113.    Возвращает два словаря:
  114.      col_matches: {имя_столбца: количество совпадений}
  115.      col_matches_rows: {имя_столбца: список индексов строк, где найдены совпадения}
  116.    """
  117.     col_matches = {col: 0 for col in dataframe.columns}
  118.     col_matches_rows = {col: [] for col in dataframe.columns}
  119.     for token in tokens:
  120.         # Создаем шаблон для точного совпадения по слову с учетом границ слова
  121.         pattern = re.compile(r'\b' + re.escape(token) + r'\b')
  122.         for col in dataframe.columns:
  123.             for idx, value in dataframe[col].items():
  124.                 if pd.notnull(value) and pattern.search(str(value).lower()):
  125.                     col_matches[col] += 1
  126.                     col_matches_rows[col].append(idx)
  127.     return col_matches, col_matches_rows
  128.  
  129.  
  130. def analyze_results_all(col_matches_rows, dataframe, original_query, locations_found):
  131.     """
  132.    Анализирует результаты поиска.
  133.  
  134.    Сценарий 1: Если по столбцу "наименование" найдены совпадения, формирует отформатированную справку:
  135.       💊 *Название:* <наименование>
  136.          📁 *Раздел:* <раздел>
  137.          📂 *Подраздел:* <подраздел>
  138.          🔹 *Фармакологическое действие:* <...>
  139.          🔹 *Показания к применению:* <...>
  140.          🔹 *Побочные эффекты:* <...>
  141.  
  142.    Сценарий 2: Если препарата не найдено, но есть совпадения в "Показаниях к применению" или "Побочных эффектах",
  143.    формируется сообщение вида:
  144.       "Возможно, у вас наблюдаются симптомы: <original_query>
  145.        ⚠️ Это может быть побочным эффектом препарата: <перечень препаратов>
  146.        💉 При таких симптомах показания по лечению: <перечень препаратов>"
  147.  
  148.    Если совпадений нет, возвращается сообщение "Информация по запросу не найдена."
  149.  
  150.    Дополнительно, если обнаружены локализации, связанные с травматологией (например, "рука" или "кости"),
  151.    к сообщению добавляется рекомендация обратиться к травматологу.
  152.  
  153.    Изменения: строка примерно 110.
  154.    """
  155.     # Сценарий 1: Поиск по "наименование"
  156.     if col_matches_rows.get("наименование") and len(col_matches_rows["наименование"]) > 0:
  157.         matched_indices = set(col_matches_rows["наименование"])
  158.         result_lines = []
  159.         for idx in matched_indices:
  160.             row = dataframe.loc[idx]
  161.             med_name = row.get("наименование", "Без наименования")
  162.             section = row.get("раздел", "нет данных")
  163.             subsection = row.get("подраздел", "нет данных")
  164.             pharm_action = row.get("Фармакологическое действие", "нет данных")
  165.             indications = row.get("Показания к применению", "нет данных")
  166.             side_effects = row.get("Побочные эффекты", "нет данных")
  167.             line = (f"💊 *Название:* {med_name}\n"
  168.                     f"   📁 *Раздел:* {section}\n"
  169.                     f"   📂 *Подраздел:* {subsection}\n"
  170.                     f"   🔹 *Фармакологическое действие:* {pharm_action}\n"
  171.                     f"   🔹 *Показания к применению:* {indications}\n"
  172.                     f"   🔹 *Побочные эффекты:* {side_effects}")
  173.             result_lines.append(line)
  174.         message_text = "Возможно, вы искали:\n\n" + "\n\n".join(result_lines)
  175.     else:
  176.         # Сценарий 2: Поиск по симптомам в "Показаниях к применению" и "Побочных эффектах"
  177.         side_effects_indices = set(col_matches_rows.get("Побочные эффекты", []))
  178.         indications_indices = set(col_matches_rows.get("Показания к применению", []))
  179.         messages = []
  180.         if side_effects_indices:
  181.             meds_side = []
  182.             for idx in side_effects_indices:
  183.                 row = dataframe.loc[idx]
  184.                 med_name = row.get("наименование", "Неизвестно")
  185.                 meds_side.append(med_name)
  186.             meds_side = list(set(meds_side))
  187.             messages.append("⚠️ Это может быть побочным эффектом препарата: " + ", ".join(meds_side))
  188.         if indications_indices:
  189.             meds_ind = []
  190.             for idx in indications_indices:
  191.                 row = dataframe.loc[idx]
  192.                 med_name = row.get("наименование", "Неизвестно")
  193.                 meds_ind.append(med_name)
  194.             meds_ind = list(set(meds_ind))
  195.             messages.append("💉 При таких симптомах показания по лечению: " + ", ".join(meds_ind))
  196.         if messages:
  197.             message_text = (f"Возможно, у вас наблюдаются симптомы: *{original_query}*.\n" +
  198.                             "\n".join(messages))
  199.         else:
  200.             message_text = "Информация по запросу не найдена."
  201.     # Если в найденных локациях обнаружены слова, связанные с травматологией, добавляем рекомендацию
  202.     if any(loc in ['рука', 'кости'] for loc in locations_found):
  203.         message_text += "\n\nРекомендуем также обратиться к травматологу для уточнения диагноза."
  204.     return message_text
  205.  
  206.  
  207. @bot.message_handler(commands=['start', 'help'])
  208. def send_welcome(message):
  209.     bot.reply_to(message, 'Привет! Чем могу помочь? Напиши "Выход" для завершения работы.')
  210.  
  211.  
  212. @bot.message_handler(content_types=['text'])
  213. def get_text_messages(message):
  214.     if message.text.strip().lower() == "выход":
  215.         bot.send_message(message.from_user.id, "Бот завершает работу. До свидания!")
  216.         exit_program()
  217.         return
  218.     original_query = message.text.strip()
  219.     # Получаем леммы и найденные локации
  220.     lemmas, locations_found = process_message(message.text)
  221.     print("Обработанные токены:", lemmas, "Найденные локации:", locations_found)
  222.     # Если запрос содержит "боль", но не указана локализация, просим уточнить
  223.     if "боль" in lemmas and not locations_found:
  224.         bot.send_message(message.from_user.id,
  225.                          "Пожалуйста, уточните локализацию боли (например, 'боль в животе' или 'боль в руке').")
  226.         return
  227.     if df is None:
  228.         bot.send_message(message.from_user.id, "Ошибка: не удалось загрузить таблицу с данными.")
  229.         return
  230.     col_matches, col_matches_rows = search_tokens_in_table(lemmas, df)
  231.     print("Совпадения по столбцам:", col_matches)
  232.     result = analyze_results_all(col_matches_rows, df, original_query, locations_found)
  233.     bot.send_message(message.from_user.id, result, parse_mode="Markdown")
  234.  
  235.  
  236. def exit_program():
  237.     print("Завершение работы программы...")
  238.     bot.stop_polling()
  239.     quit()
  240.  
  241.  
  242. bot.polling(none_stop=True, interval=0)
  243.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement