Как стать автором
Обновить

Как я написала чат-бот для телеги на питоне и задеплоила его на удаленную машину за один день

Время на прочтение6 мин
Количество просмотров21K

Всем привет! Меня зовут Катя и я backend разработчик. Пишу в основном на java, иногда на kotlin. В один прекрасный день мне пришла в голову мысль написать чат-бота для телеги. В силу специфики своей работы я начала с написания spring boot приложения на java, потом меня понесло в прикручивание БД, медленно все перетекло в тему создания сервиса как такового и до момента собственно вызова рестов ботом телеграмм не дошло 😅.

В следующем подходе я подумала: а почему java, давай возьмем python. У меня был небольшой опыт написания на этом языке - и я решилась - начну снова с "Здравствуйте, мир!" только сразу в рамках проекта чат-бот телеграмм. На просторах интернета нашла крутую библиотеку с звучным именем natasha. Идея бота следующая: вы скачиваете свой чатик из одного ныне запрещенного мессенджера и этот .txt файл отдаете боту. Он находит ТОП 10 существительных и глаголов из вашего чата 🤓. Далее пройдемся по шагам.

Организационная часть

  1. Найти в телеграмм пользователя https://t.me/BotFather - он бог всех ботов.

  2. Передать ему команду /newbot для создания нового бота. Назвать своего бота и обязательно концовка имени должна быть Bot. Имя моего - analyzeYourChatBot

  3. В ответном сообщении отец ботов вышлет вам токен доступа к API.

  4. Так же можно сразу задать описание вашему боту путем вызова команды /setdescription а так же поставить аватар /setuserpic

Функционал бота

Вернемся к идее бота: взять .txt-файл, считать содержимое, очистить текст от ненужных штук, разбить на слова, понять что из них существительные, а что глаголы, подсчитать количество каждых и вывести ТОП 10.

Итак, основной участник действий - функция analyze, которая примет в себя контент файла. Далее построчно:
46 - приведем все слова в нижний регистр
47 - избавимся от текста с паттерном даты и отправителя сообщения вида [16.09.2021, 17:10:20] Katerina: А что осталось сегодня? Тестировала паттерн в удобном онлайн-сервисе regex101
48 - оставим в тексте только русские слова и пробелы
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

Заключительное слово

В целом мне очень понравилось писать на питоне - это быстро и очень увлекательно. Докер как всегда рулит. Спасибо за внимание и можно кидать в меня помидорами в коментах 💋.

Теги:
Хабы:
Всего голосов 39: ↑19 и ↓20-1
Комментарии20

Публикации

Истории

Работа

Data Scientist
60 вакансий
Python разработчик
136 вакансий

Ближайшие события