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


Всем привет! Меня зовут Катя и я 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

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

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




Комментарии (13):

  1. webhamster
    /#24761476 / +10

    Всем привет! Меня зовут Катя

    Чат-бот по определению ТОП слов из вашего чата запрещённого мессенджера «на коленках»

    На коленке, Катя. Правильно говорить "на коленке".

    На коленках сидят у кого-либо. На коленке пишут что-то по-быстрому.

    • ipatov_dn
      /#24763606

      Ну не удобно ей на одной писать, девушка, ножки стройные(я предпологаю) поэтому и на коленках

    • vrestles
      /#24763890 / +2

      спасибо, поправила. узнала что-то новое ;) не была в курсе, что есть различия

  2. Rad_66
    /#24762350 / +1

    И к чему минус и такие комментарии? человек поделился своим наработками хорошо сделал плохо он поделился а тут )) такое ощущение что тут не сообщество близких по интересам людей о клуб элитных ХАХЕРОФ негоже)

    • 0xf331d34d
      /#24763502 / +3

      Объясню почему минусы.

      Название статьи сходу отдает неким хвастовством - "За один день". Вау, невероятно, мы рады, но зачем здесь такое на Хабре? Смайлики и всякие лол зачастую идут туда же.

      Сама статья по себе hello world бот на питоне, коих тут и так тысячи, ничего нового не привнесла.

      • vrestles
        /#24763588 / +6

        Спасибо, поняла. Согласна, тема статьи не нова. А заголовок и посыл статьи в том, что не стоит боятся начать что-то писать: 1) это не так сложно 2) это не так долго. Скорее она более мотивационная, чем новаторская. Но я поняла, что это статья не для хабра

        • temamagic
          /#24764772

          Кажется, что вы просто не совсем поняли сами, для кого пишете эту статью.

          Для специалиста ничего нового. Для новичка почему-то кажется что потребуется больше деталей. Мотивационная часть выражена слабо, либо я не заметил.

          Про хвастовство сказали выше.

          Я такой себе писатель, но вот +- средний сценарий для такой статьи:

          1) introduce yourself (Я - Катя...)

          2) Боль которая есть сейчас (???, допустим, страстное желание знать топ 10 по существительным и глаголам в том самом месенджере)

          3) Как сделать (Телеграм бот с разделением слов на сущ. и глаг. с либой natasha)

          4) Драма (Чорт, знаю только java и котлин)

          5) Мотивация (Задачка на день, новый амбициозный проект, кину вызов себе и изучу питон ради natasha)

          6) Результат что получилось и как, что открыла для себя нового (не просто "мне понравилось", а наверняка узнала что-то из особенностей работы), сколько потратила чашек кофе

          При этом код желательно прятать под кат (чтобы не читать полотно кода кому не интересно) и разбавить картинками (хотя бы пример работы бота, вы же его проверили в бою?)

          Ну и я ожидал что с таким заголовком в конце будет какой-то вау, в духе что вы это все сделали за 1 день и еще и бот стрельнул где-то)

          Заметка для хозяюшки:

          Для ботов на питоне, в будущем, лучше взять aiogram, она во многом удобнее, приятнее и у нее есть большое русскоговорящее коммьюнити

          • sunsexsurf
            /#24765056

            Меня смутило именно то, что было написано «чат-бот». Я искренне думал, что автор прикрутит NLP-модели типа Берт или ДипПавлов для того, чтобы «поддерживать диалог». А так - тупо txt передали и там уже Наташа все посчитала. Кстати, ради такого тащить в проект целую библиотеку - не самое лучшее решение, как по мне.

            • vrestles
              /#24765130

              Да, бот очень тупой, а так просто посмотрела на возможности Наташи ) но я не специалист в области data science. В начале статьи написала, что я занимаюсь вообще другим. Видимо лучше писать статью по своему профилю)

          • vrestles
            /#24765120

            Благодарю за обратную связь! Особенно приятно получить развернутый ответ от профессионала в этой области. Учту ваши советы при написании следующей статьи. Не особо удачный выход на просторы хабра =)

  3. lepaapel
    /#24763504

    2,3 добавляю файлы с двумя написанными на предыдущих шагах скриптами
    3,4 устанавливаю необходимые библиотеки для работы приложения
    6 запускаю код

     подправьте плз, номера
    и спасибо за статью