Как я написала чат-бот для телеги на питоне и задеплоила его на удаленную машину за один день
Всем привет! Меня зовут Катя и я backend разработчик. Пишу в основном на java, иногда на kotlin. В один прекрасный день мне пришла в голову мысль написать чат-бота для телеги. В силу специфики своей работы я начала с написания spring boot приложения на java, потом меня понесло в прикручивание БД, медленно все перетекло в тему создания сервиса как такового и до момента собственно вызова рестов ботом телеграмм не дошло ?.
В следующем подходе я подумала: а почему java, давай возьмем python. У меня был небольшой опыт написания на этом языке - и я решилась - начну снова с "Здравствуйте, мир!" только сразу в рамках проекта чат-бот телеграмм. На просторах интернета нашла крутую библиотеку с звучным именем natasha. Идея бота следующая: вы скачиваете свой чатик из одного ныне запрещенного мессенджера и этот .txt
файл отдаете боту. Он находит ТОП 10 существительных и глаголов из вашего чата ?. Далее пройдемся по шагам.
Организационная часть
Найти в телеграмм пользователя https://t.me/BotFather - он бог всех ботов.
Передать ему команду
/newbot
для создания нового бота. Назвать своего бота и обязательно концовка имени должна быть Bot. Имя моего -analyzeYourChatBot
В ответном сообщении отец ботов вышлет вам токен доступа к API.
Так же можно сразу задать описание вашему боту путем вызова команды
/setdescription
а так же поставить аватар/setuserpic
Функционал бота
Вернемся к идее бота: взять .txt
-файл, считать содержимое, очистить текст от ненужных штук, разбить на слова, понять что из них существительные, а что глаголы, подсчитать количество каждых и вывести ТОП 10.
Итак, основной участник действий - функция analyze
, которая примет в себя контент файла. Далее построчно: 46
- приведем все слова в нижний регистр47
- избавимся от текста с паттерном даты и отправителя сообщения вида [16.09.2021, 17:10:20] Katerina: А что осталось сегодня?
Тестировала паттерн в удобном онлайн-сервисе regex10148
- оставим в тексте только русские слова и пробелы49
- схлопнем несколько пробелов один51-61
- копипаст кода из примеров использования библиотеки natasha. Если очень вкратце, то мы разделили текст на слова и определили какое слово существительное, а какое - глагол.
Далее формируем структуру данных с существительными и глаголами, находим количество каждого вхождения, сортируем и оставляем ТОП 10.
#!/usr/bin/env python3
import re
from string import whitespace
from collections import defaultdict
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
Doc
)
CYRILLIC_LETTERS = u"абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
TOP_COUNT = 10
def strip(text):
allowed_chars = CYRILLIC_LETTERS + whitespace
return "".join([c for c in text if c in allowed_chars])
def sort(data):
result_dict = defaultdict(int)
for key in data:
result_dict[key]+=1
return sorted(result_dict.items(), key=lambda kv: kv[1], reverse=True)
def make_str(data):
result = ""
top_data = data[:TOP_COUNT]
for key, value in top_data:
result += key
result += ": "
result += str(value)
result += "\n"
return result
def find(data, type):
result = []
for item in data:
if (item.pos == type):
result.append(item)
return result
def analyze(data):
text = data.lower()
text = re.sub(r'\[\d{2}\.\d{2}\.\d{4}\, \d{2}:\d{2}:\d{2}\] .+?:', '', text, flags=re.MULTILINE)
text = strip(text)
text = re.sub(' +', ' ', text)
segmenter = Segmenter()
morph_vocab = MorphVocab()
emb = NewsEmbedding()
morph_tagger = NewsMorphTagger(emb)
doc = Doc(text)
doc.segment(segmenter)
doc.tag_morph(morph_tagger)
for token in doc.tokens:
token.lemmatize(morph_vocab)
nouns_dict = find(doc.tokens, 'NOUN')
verbs_dict = find(doc.tokens, 'VERB')
nouns_lemmas = [_.lemma for _ in nouns_dict]
verbs_lemmas = [_.lemma for _ in verbs_dict]
sorted_nouns_dict = sort(nouns_lemmas)
sorted_verbs_dict = sort(verbs_lemmas)
res_nouns = make_str(sorted_nouns_dict)
res_verbs = make_str(sorted_verbs_dict)
result = "ТОП " + str(TOP_COUNT) + " существительных в вашем чате:\n"
result += res_nouns
result += "\n"
result += "ТОП " + str(TOP_COUNT) + " глаголов в вашем чате:\n"
result += res_verbs
return result
Вызов функционала бота
В отдельном файле внутри папки проекта будут собраны методы-хендлеры (приемщики определенных команд). Я выбрала библиотеку pyTelegramBotAPI чтобы быстрее и эффективнее взаимодействовать с API телеги.
9
определяем константу с токеном, который нам прислал отец ботов11
создаем инстанс объекта Telebot
64
запускаем этот инстанс в работу
Пишем простенькие функции на команды /start
и /help
которые просто отвечают простыней текста. Основной участник тут - get_document
. В функции мы получим документ, проверим что он формата .txt, далее считаем содержимое из него и вызовем функцию из уже готового файла с предыдущего шага.
#!/usr/bin/env python3
import telebot
import requests
import analyze_chat
import uuid
import os
TOKEN = "blablabla"
bot = telebot.TeleBot(TOKEN)
@bot.message_handler(commands=['start'])
def send_welcome(message):
bot.send_message(
message.chat.id,
'Привет!✌️ Я бот, который проанализирует твой диалог из WhatsApp \n' +
'(принадлежит признанной на территории РФ экстремисткой организации Meta). \n' +
'Скачай диалог (только на русском языке ??) и пришли боту в формате .txt.\n' +
'Для того, чтобы посмотреть как скачать весь диалог пройди на комманду /help.\n' +
'⚡️ Предупреждение! Ваши диалоги не сохраняются на стороне приложения, ' +
'однако потенциально могут быть перехвачены злоумышленниками по сети!'
)
@bot.message_handler(commands=['help'])
def get_help(message):
bot.send_message(
message.chat.id,
'1) Пройдите в чат, который хотите проанализировать, из WhatsApp ' +
'(принадлежит признанной на территории РФ экстремисткой организации Meta). \n' +
'2) Провалитесь в настройки чата \n' +
'3) Нажмите "Экспортировать чат (Export chat)" -> Без медиа -> Сохраните на компьютер\n' +
'4) Разархивируйте и пришлите боту содержимое архива (по одному файлу если их несколько)'
)
@bot.message_handler(content_types=['document'])
def get_document(message):
if (message.document.mime_type != 'text/plain'):
bot.reply_to(message, "Вы прислали файл, но он не в формате .txt ? Исправьте это недоразумение, пожалуйста")
else:
return analyze(message)
def analyze(message):
file_info = bot.get_file(message.document.file_id)
response = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(TOKEN, file_info.file_path))
random_file_name = "response_" + str(uuid.uuid4()) + ".txt"
with open(random_file_name, "wb") as f:
f.write(response.content)
text = get_text_from_file(random_file_name)
result = analyze_chat.analyze(text)
os.remove(random_file_name)
bot.reply_to(message, result)
def get_text_from_file(file_name):
f = open(file_name, "r", encoding="utf-8")
text = f.read()
return text
@bot.message_handler(func=lambda message: True)
def echo_all(message):
bot.reply_to(message, "Я могу только принять файл в формате .txt ? Кажется это не он")
bot.infinity_polling()
Деплой этого всего дела
Итак, у нас имеется код, но он работает только тогда, когда у вас открыт ноут (лол). Вернемся на шаг назад и я скажу вам, что чтобы код работал - нужно установить две библиотеки:
pip install pyTelegramBotAPI
pip install natasha
Ну и конечно должен быть установлен python ?. Теперь представьте, что все это надо будет установить на удаленной машине. Вспомним про докер. И используем силу докер-файла. Добавляю внутрь папки проекта Dockerfile
и пишу следующее:1
делаю образ на основе официального от питона2,3
добавляю файлы с двумя написанными на предыдущих шагах скриптами4,5
устанавливаю необходимые библиотеки для работы приложения6
запускаю код
FROM python:3
ADD chatbot.py .
ADD analyze_chat.py .
RUN pip install pyTelegramBotAPI
RUN pip install natasha
CMD python3 ./chatbot.py
Далее создаем кастомный образ приложения на основе докер-файла:
docker build -t vrestles/analyze-chat-bot:latest -f Dockerfile .
Этот образ я переношу в докер-хаб в качестве приватного образа:
docker login -u vrestles -p *****
docker push vrestles/analyze-chat-bot:latest
Заметьте, что сначала я залогинилась и потом запушила образ.
Далее прохожу на удаленную машину - там уже установлен докер. И только он один кстати говоря. И все что мне нужно сделать опять залогиниться в докер и запустить приватный образ:
docker run -d vrestles/analyze-chat-bot:latest
К слову говоря, при запуске он сам поймет, что образ не скачан и пойдет сам за ним. Запуск произведен в дитач-режиме, что значит что он произведет запуск в фоне и отдаст только айдишник контейнера. Посмотреть запущенные контейнеры можно при помощи команды docker ps
Заключительное слово
В целом мне очень понравилось писать на питоне - это быстро и очень увлекательно. Докер как всегда рулит. Спасибо за внимание и можно кидать в меня помидорами в коментах ?.