В контексте всеобщего хайпа на Коронавирусе, я решил сделать хоть что-нибудь полезное (но не менее хайповое). В данной статье я расскажу о том, как за 2.5 часа (именно столько у меня ушло) создать и развернуть Telegram Бота с использованием Rule-Based NLP методов, отвечающего на FAQ-вопросы на примере с кейсом COVID-19.
В ходе работы, мы будем использовать старый добрый Python, Telegram API, пару стандартных NLP-библиотек, а также Docker.
pip install python-telegram-bot --upgrade
def start(update, context):
#обработка команды запуска бота
pass
def help(update, context):
#обработка команды помощи
pass
def message(update, context):
#обработка текстового сообщения
pass
def error(update, context):
#обработка ошибки
pass
def get_answer():
"""Start the bot."""
# Create the Updater and pass it your bot's token.
# Make sure to set use_context=True to use the new context based callbacks
# Post version 12 this will no longer be necessary
updater = Updater("Token", use_context=True)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", help))
# on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, message))
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Run the bot until you press Ctrl-C or the process receives SIGINT,
# SIGTERM or SIGABRT. This should be used most of the time, since
# start_polling() is non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == "__main__":
get_answer()
updater.start_polling()
);def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text("""
Привет!
Я могу проконсультировать тебя по любому вопросу о COVID-19.
Например:
- *Как передается коронавирус?*
- *Защищает ли маска?*
- *Какие сейчас страны риска?*
и т.д.
Просто спроси!
""", parse_mode=telegram.ParseMode.MARKDOWN)
def help(update, context):
"""Send a message when the command /help is issued."""
update.message.reply_text("""
Спрашивай меня о чём хочешь (в рамках COVID-19).
Например:
- *Как передается коронавирус?*
- *Защищает ли маска?*
- *Какие сейчас страны риска?*
и т.д.
Просто спроси!
""", parse_mode=telegram.ParseMode.MARKDOWN)
parse_mode=telegram.ParseMode.MARKDOWN
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
{
"Что такое коронавирус и как происходит заражение?": "Новый коронавирус — респираторный вирус. Он передается главным образом воздушно-капельным путем в результате вдыхания капель, выделяемых из дыхательных путей больного, например при кашле или чихании, а также капель слюны или выделений из носа. Также он может распространяться, когда больной касается любой загрязненной поверхности, например дверной ручки. В этом случае заражение происходит при касании рта, носа или глаз грязными руками.",
"Какие симптомы у коронавируса?": "Основные симптомы коронавируса:\n Повышенная температура\n Чихание\n Кашель\n Затрудненное дыхание\n\nВ подавляющем большинстве случаев данные симптомы связаны не с коронавирусом, а с обычной ОРВИ.",
"Как передается коронавирус?": "Пути передачи:\nВоздушно-капельный (выделение вируса происходит при кашле, чихании, разговоре)\nКонтактно-бытовой (через предметы обихода)",
}
import json
from fuzzywuzzy import fuzz
import pymorphy2
#создание объекта морфологического анализатора
morph = pymorphy2.MorphAnalyzer()
#загрузка базы знаний
with open("faq.json") as json_file:
faq = json.load(json_file)
def classify_question(text):
#лемматизация текста юзера
text = ' '.join(morph.parse(word)[0].normal_form for word in text.split())
questions = list(faq.keys())
scores = list()
#цикл по всем вопросам из базы знаний
for question in questions:
#лемматизация вопроса из базы знаний
norm_question = ' '.join(morph.parse(word)[0].normal_form for word in question.split())
#сравнение вопроса юзера и вопроса из базы знаний
scores.append(fuzz.token_sort_ratio(norm_question.lower(), text.lower()))
#получение ответа
answer = faq[questions[scores.index(max(scores))]]
return answer
def dump_data(user, question, answer):
username = user.username
full_name = user.full_name
id = user.id
str = """{username}\t{full_name}\t{id}\t{question}\t{answer}\n""".format(username=username,
full_name=full_name,
id=id,
question=question,
answer=answer)
with open("/data/dump.tsv", "a") as myfile:
myfile.write(str)
def message(update, context):
"""Answer the user message."""
#получение ответа
answer = classify_question(update.message.text)
#сохранение в файл
dump_data(update.message.from_user, update.message.text, answer)
#отправка сообщения
update.message.reply_text(answer)
#образ
FROM python:3.6.6-slim
#название рабочей директории
WORKDIR /home/alex/covid-bot
#копируем файл requirements.txt
COPY requirements.txt ./
# Install required libs
RUN pip install --upgrade pip -r requirements.txt; exit 0
#копируем папку в которой будут наши данные
COPY data data
# Копирование файлов проекта
COPY app.py faq.json reply_generator.py boot.sh ./
# На всякий пожарный
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
#раздаём права
RUN chmod +x boot.sh
#указываем входную точку
ENTRYPOINT ["./boot.sh"]
#версия docker-compose
version: '2'
#список запускаемых сервисов
services:
bot:
restart: unless-stopped
image: covid19_rus_bot:latest
container_name: covid19_rus_bot
#задаём переменную среды для boot.sh
environment:
- SERVICE_TYPE=covid19_rus_bot
#пробрасываем volume для доступа к папке с данными
volumes:
- ./data:/data
#!/bin/bash
if [ -n $SERVICE_TYPE ]
then
if [ $SERVICE_TYPE == "covid19_rus_bot" ]
then
exec python app.py
exit
fi
else
echo -e "SERVICE_TYPE not set\n"
fi
sudo docker build -t covid19_rus_bot:latest .
sudo docker-compose up
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
К сожалению, не доступен сервер mySQL