Cat

Вхожу в IT - telegram-bot (часть 2)

Данная статья продолжает цикл статей о том как я вхожу в IT product уровня в сфере Telegram-ботов на aiogram3. Тут я описываю сложности вхождения в коммерческий мир IT, высказываю свои мысли, рассказываю через какие трудности приходится пройти на старте продуктовой карьеры в IT.

Путь в IT Arduinum628 13 Ноябрь 2024 Просмотров: 111

Вступление

Всем доброго дня! В предыдущей части статьи Вхожу в IT - telegram bot (часть 1) я рассказывал о начале своего пути в разработке, так же затронул начало разработки бота product уровня.

Сегодня мы с вами увидим что у меня получилось в боте первой версии и узнаем с какими трудностями мне пришлось столкнуться в процессе написания, затронем процессы взаимодействия с более опытным разработчиком и сам процесс код ревью. Отложите свои IDE сегодня вам не придётся писать код, зато вы узнаете много нового о самом процессе становления разработчика. Я надеюсь что моя история вдохновит кого-то продолжать упорно продвигаться по карьерной лестнице в мире IT. Налейте себе чайку и давайте продолжим путь становления =)

 

Улучшение кода и менторство

В конце предыдущей статьи я рассказывал что Иван, дал мне ТЗ Telegram-бота интернет-магазина от которого отказался заказчик.

На фрилансе такое иногда бывает что по каким-то причинам заказ не переходит стадию ТЗ или стадию написания документа, в котором описывается стек, стоимость работ и прочее. Какими бы причины отказа не были у вас остаётся ТЗ, которое является отличной возможностью кому-то потренироваться в написании кода. Можно сказать это бизнес задача, которую можно сделать неплохим пет проектом в портфолио. Возможно особо упорные могут вывести такой проект в настоящую product разработку. Кто знает возможно я стану одним из них =)

Если у вас нет опыта это гораздо лучше, чем написание учебного проекта на обучении. Обычных учебных проектов очень много и они не ценятся как опыт. Скорее будет цениться у работодателя тот факт, что вы закончили обучение. Поэтому если вам нужен опыт после курсов, то смело ищите подобные ТЗ или идите на хакатон.

 

ТЗ проекта

Ну вот вы получили ТЗ проекта, а что же делать дальше? Логично будет написать работающую хоть как-то версию. К сожалению в большинстве случаев у новичка нет необходимых знаний чтоб такую первую версию положить в портфолио и показать работодателю. Скорее всего работодатель посчитает код недостаточно хорошим. Возможно код оформлен небрежно, неэффективен, устарел, труден в расширении функционала и тд.

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

 

Менторство

Поговорим о менторстве и для чего это нужно.

Ментор — это человек с значительным опытом в разработке продуктов в нужной вам области. Он готов делиться своими знаниями и поддерживать вас на пути к профессиональному росту.

Обычно за менторство платят деньги или предоставляют что-то полезное в обмен на знания. Это может быть услуга или просто желание улучшить навыки менторства самому. В любом случае, найти такого человека, который поможет вам улучшить свой код, — это очень ценная возможность.

По поводу менторства поговорили. Теперь давайте узнаем с чего я начал писать бота и с какими трудностями мне пришлось столкнуться в процессе написания кода.

 

Структура проекта

После того как вы получили ТЗ - внимательно с ним ознакомьтесь. После этого обязательно задайте уточняющие вопросы (обычно это вопросы по функционалу бота/сайта/программы). Даже если всё кажется простым и понятным, уточняющие вопросы помогут избежать "додумывания" и последующей переделки, если окажется, что заказчик хотел иного решения.

Так случилось и в моём случае: я уточнил что мне было непонятно и вписал в ТЗ ответы. Например, я уточнил информацию по поводу админки. Оказалось её нужно реализовывать через стандартные средства бота, а не через html страницу в Web App.

Web App позволяет открывать веб страницу прямо в боте во внутреннем браузере Telegram.

 

Если с вами общается разработчик вы так же можете уточнить детали по стеку. Спросите обязательно про версию языка, на котором вы пишете, про библиотеки, базы данных. Так у вас сформируется картина на каком стеке вы будете писать. И таких вопросов может быть довольно много. Чем больше вы будете знать о задаче тем лучше и удачнее для заказчика/работодателя вы её выполните. После всех вопросов я получил гораздо большее представление о боте, который мне предстояло написать.

 

Начало работы

Дальше я открыл VSCode и принялся за начало работы.

В тот момент я ещё не знал сколько мне ещё предстоит запнуться и переписать кода =)

Начал я с создания виртуального окружения с помощью pyenv и установки нужных библиотек обычным pip. О дальнейшей судьбе pip в проекте, его плюсах и минусах поговорим в третьей части статьи. Пока же я делал то к чему привык и шёл как я думал в нужном мне направлении.

После установки библиотек я принялся писать структуру проекта. В этом случае ментор Иван дал мне скриншот образца как это примерно должно у меня выглядеть. Так же он мне дал совет, что хорошей практикой является изучение структур product уровня проектов на github.

До этого я много писал для фреймворке Django и там изначально со структурой проекта было гораздо более понятно. Там просто многое создаётся командами и фреймворк диктует правила по структуре проекта.

Но принцип один и тот же: у вас есть главная папка проекта в которой есть пакеты с вашими app приложениями. В каждом app вы ещё создаёте пакеты для разделения логики. Чтоб у вас, например, были handler и keyboard файлы в разных пакетах. 
Дальше, например, в пакете handlers вы создаёте .py-файлы так, чтоб разделить логику этих пакетов. Например, 2 файла shop_commands.py и admin_commands.py.

Такой принцип структурирует модули и пакеты, разделяет логику и уменьшает количество кода в каждом файле. Ниже я вам скину простой пример того что я имею в виду под структурой проекта. Она примитивная и может отличаться от ваших проектов, но наглядно показывает как положить всё на свои места.

your_bot/
├── alembic/ 
├── your_bot/ 
│ ├── __init__.py
│ ├── main.py 
│ ├── config.py 
│ ├── handlers/ 
│ │ ├── __init__.py 
│ │ ├── shop_commands.py 
│ │ └── admin_commands.py 
│ ├── database/ 
│ │ ├── __init__.py 
│ │ └── shop_model.py
│ ├── keyboards/ 
│ │ ├── __init__.py 
│ │ ├── shop_keyboards.py
│ │ └── admin_keyboards.py
│ ├── utils/ 
│ │ ├── __init__.py 
│ │ ├── utils.py 
│ │ └── logger.py 
├── README.md 
└── .env

 

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

Если вы очень долго листаете код в поисках нужного класса или функции, то вам нужно задуматься над разделением .py-файла на два или больше. Часто размеры проекта мы осознаём уже тогда, когда код разросся и нам становится неудобно его расширять или изменять. Я тоже часто разделяю код на файлы, когда в этом уже появляется необходимость.

 

База данных

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

В требованиях к моему проекту указана реляционная база данных PostgreSQL.

 

Выбор способа запуска БД

Для разработки локально есть выбор.

  1. Поднять контейнер в Docker с базой данных, которая будет создана с пользователем и паролем для вас пустой
  2. Скачать установщик с официального сайта и использовать базу данных установленную на ПК.

Скачивать установщик есть смысл только в том случае если вы ещё не умеете работать с Docker и только учитесь писать сам код. В остальных случаях это лишний мусор на компьютере, которым не стоит нагружать вашу операционную систему.

Овладение Docker один из самых важных навыков для product разработки. Новичкам я оставлю курс по Docker в ссылках к статье. Так же на сайте Код на салфетке тоже есть гайды и полезные статьи по Docker ссылки на них я тоже оставлю. Я не буду показывать как создать контейнер с базой данных PostgreSQL.

 

Переменные окружения

И так я создал контейнер с базой и запустил его. Теперь нужно обязательно настроить конфиг, где будут храниться настройки для подключения к БД.

Я раньше не задумывался, что используя .env напрямую подвергал свой проект лишним уязвимостям. Так же я проверял на типы данных в обычных if-условиях или в try-except-блоках при подключении к БД. Многие переменные из .env не проверял даже на типы данных.

В требованиях к проекту мне указали на необходимости использования pydantic-settings для конфига. Это отличная библиотека, которая позволит вам создавать удобные и безопасные классы конфигурации для своего проекта, удобно читать .env, валидировать типы данных в конфиге и многое другое. В product проектах очень важно писать удобный и безопасный конфиг поэтому я рекомендую овладеть данной библиотекой.

 

Модели БД

Далее мне предстояло написать модели используя ORM подход в SQLAlchemy. Там естественно тоже есть ряд нюансов, о которых я не знал на тот момент.

Чтоб стать разработчиком product уровня нужно выветрить из головы: "пишу код первым попавшимся способом, который работает". Когда вы что-то делаете в первый раз это происходит не всегда идеально. Ниже я вам покажу пример плохой модели для базы данных на базе моего кода до первого код ревью.

Нужно привыкнуть к парадигме того что после написания кода нужно посмотреть какие есть ещё способы, посмотреть недостатки и улучшить ваш код если он не достаточно хорош. Взгляните на код внизу и подумайте почему он плохой? Мы не берём отсутствие некоторых важных полей например is_active, а возьмём лишь недостатки того кода, который написан в примере.

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, Integer, String


class Base:
    """Класс для корректной работы аннотаций"""

    __allow_unmapped__ = True


Base = declarative_base(cls=Base)


class Test(Base):
    """Модель для теста"""

    __tablename__ = 'test'

    id: int = Column(
        Integer,
        primary_key=True,
        name='id'
    )

    title: str = Column(
        String(length=200),
        name='название',
        nullable=False
    )

 

Код ревью и рефакторинг своего кода

У меня нет цели показать вам всё ревью или все ситуации в моём коде, где я сам улучшил код или переписал код по просьбе ментора - это заняло бы много времени, а статьи растянулись бы на несколько месяцев. Вместо этого я покажу несколько конкретных примеров улучшения и результат, который получился в итоге. Так же вы приобретёте некий способ мысли и навык, который позволит вам писать код самостоятельно.

Многие менторы учились самостоятельно, разбирая нюансы кода, многим просто лень использовать такой подход, а у многих нет времени. Часто задача, ограниченная по времени не даёт нам её шлифовать до совершенства.

У меня есть друг Александр, который работает фронтендером уже не первый год. Он подтверждает что иногда не успевает написать код идеально из-за того что задача ограничена по времени. В этом случае старшие разработчики обычно делают ему код ревью если что-то нужно поправить. Правда сейчас он вроде как middle и единственный фронтендер на проекте, поэтому по всей видимости, код ревью для его кода делать больше некому.

Как я заметил, хороший специалист почти сразу находит лучший вариант ещё до того как он написал код, поэтому нужно найти золотую середину и как можно быстрее понять какой вариант взять в реализацию. Вы должны успевать делать свой код в срок и стараться делать его максимально качественным.

 

Код ревью

Сейчас мне приходится буквально перелопачивать свой код. Помните что на реальном заказе или на работе у вас будет куда меньше времени чем у меня сейчас. Итак давайте обсудим то что из себя представляет код ревью и как его пишут.

Мне написали код ревью аж в двух вариантах:

  • Первый вариант это комментарии в Github, под конкретными файлами.
  • Второй вариант это ревью в формате MD. Обычно описывают место и саму проблему в коде, а так же подсказывают что лучше использовать, чтоб исправить.

При этом разработчик может либо указать пример правильного кода либо подсказать словами.

Первое ревью мне отправили досканальное с примерами правильного кода. Но лично мне не очень понравился такой вариант. Поэтому я попросил Ивана давать мне подсказки без примеров кода. Когда вам показывают пример вы не думаете своей головой, а просто переписываете код с код ревью и адаптируете его.

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

 

Исправление модели БД

Давайте разберём модель выше и поймём почему она не совсем правильная и как её можно сделать лучше. Давайте начнём с класса Base.

class Base:
    """Класс для корректной работы аннотаций"""

    __allow_unmapped__ = True


Base = declarative_base(cls=Base)

 

Класс Base - это базовый класс, от которого будет наследоваться наша модель.

Первое, что бросилось в глаза моему ментору это способ создания базового класса Base = declarative_base(cls=Base). Это не совсем удобно делать с помощью функции.

Ментор в код ревью мне посоветовал использовать наследование для создания базового класса. Я стал думать на эту тему и понял что создал лишнюю строку кода и я создаю класс функцией что лишняя операция. Вместо данной функции можно использовать уже готовый класс DeclarativeBase , который мне подсказал Иван.

DeclarativeBase нужен для создания базового класса, от которого будут наследоваться наши модели. Он обеспечивает связь между классами и таблицами в базе данных, автоматически связывает колонки и отношения, определяет, какие атрибуты должны стать столбцами таблиц и многое другое.

Давайте спустимся ниже по коду и посмотрим для чего нам __allow_unmapped__ = True. Это флаг, разрешающий классу содержать в себе атрибуты, которые не должны быть полями таблицы в БД. Зачем нам это нужно? Правильно, незачем - у нас нет таких атрибутов.

Вместо этого мы уберём этот флаг и сделаем класс Base абстрактным. Таким образом Base класс не будет являться классом модели для создания новой таблицы в БД, но будет основой для моделей.

Так же в базовом классе мы создадим поле id для того что бы не создавать в каждой модели это поле и экономить строки кода. Давайте посмотрим как теперь выглядит новый переработанный код после первого код ревью.

from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped
from datetime import datetime


class Base(DeclarativeBase):
    """Класс для корректной работы аннотаций"""

    __abstract__ = True

    id: Mapped[int] = mapped_column(
        Integer,
        primary_key=True,
        name='id'
    )


class Test(Base):
    """Модель для теста"""

    __tablename__ = 'test'

    title: str = Column(
        String(length=200),
        name='название',
        nullable=False
    )

 

Выглядит более просто для восприятия и более практично. Мы видим что класс Base стал абстрактным и что у него появилось общее поле id для всех полей моделей. А модель Test теперь не имеет id поле, а наследует его от Base.

Внимательный читатель увидит что мы используем Mapped[int] в аннотации типов вместо обычного int и mapped_column вместо Column при создании полей в БД.

Использование Mapped[int] и mapped_column позволяет использовать более строгую типизацию. Это позволяет, к примеру, понимать ошибки кода ещё при написании его в IDE до самого запуска, что экономит время разработки и отладки. Использование mapped_column() обеспечивает интеграцию с современными функциями SQLAlchemy ORM, включая управление состояниями и автоматическое обновление атрибутов.

Вроде бы это всё выглядит вполне хорошо и логично, но совершенству нет предела. В следующей статье я покажу вам как можно ещё улучшить тот код, что я вам показал. Это были простые и наглядные примеры того как можно улучшить свой код.

 

Обзор бота (версия 1)

Теперь давайте перейдём к тому что у меня получился за бот в итоге и какой функционал он имеет в "первой версии". Нам нужно запустить бота. Для этого в main.py я написал код для удобного запуска руками. Он позволит нам в удобно запускать код в IDE VSCode нажав на кнопку "Run Python File".

if __name__  '__main__':
    try:
        run(start())
    except KeyboardInterrupt:
        pass

 

Запуск бота

Иван посоветовал мне обернуть запуск в конструкцию try-except для того, чтобы при использовании комбинации CTRL + C для выключении бота не вылетала ошибка KeyboardInterrupt. Могу сказать что идея хорошая так как это не мусорит вывод терминала при выключении бота. В третьей части статьи я переделаю запуск бота, а пока я буду делать запуск руками именно так.

Давайте запустим нашего бота.

$ 2024-11-12 17:51:58 | [INFO] | shop_app.handlers.admin_commands | (admin_commands.py).start_bot(13) | Бот включен
2024-11-12 17:51:58 | [INFO] | aiogram.dispatcher | (dispatcher.py).start_polling(527) | Start polling
2024-11-12 17:51:58 | [INFO] | aiogram.dispatcher | (dispatcher.py)._polling(341) | Run polling for bot @user_name_bot id=namber_id - 'name_bot'

 

Я переименовал данные бота, что бы их скрыть это видно в строке Run polling for bot @user_name_bot id=namber_id - 'name_bot'. Так же я использую logger в проекте, который нужен чтобы удобно отслеживать события, которые происходят в вашем проекте, а так же записывать их в log-файлы.

Для этого я вам рекомендую использовать встроенный в Python модуль logging, о работе с которым вы можете узнать в документации Python.

Так же хочу сказать что в product проекте логирование обязательная и полезная вещь. Например, строка 2024-11-12 17:51:58 | [INFO] | shop_app.handlers.admin_commands | (admin_commands.py).start_bot(13) | Бот включен показывает нам дату, уровень логирования, где находится команда и текст который она выдаёт. В данном случае нас информируют о том что бот запущен.

 

Функционал

Теперь давайте перейдём в Telegram и посмотрим как выглядит бот и какие функции нам доступны в "первой версии".

На самом деле это скорее прототип и первой версией она станет лишь после второго код ревью и исправления всех проблемных мест и недоработок. Я по-быстрому озвучу для чего каждая команда и узкие места для улучшения.

И так поехали =)

 

Оповещение о запуске и команда /start

 

На первом скриншоте вы видите сообщение о том что бот включён. Эта информация отправляется только администратору бота остальные пользователи не должны видеть данное сообщение.

Далее мы видим команду /start, которая нужна для первичной информации о боте, когда человек его только запустил впервые. В первой версии она пока что работает только вручную. Во второй версии бот будет заносить пользователя в бд и запускать её автоматически если пользователя в БД нет. Так же у меня она была в меню команд, но её оттуда нужно убрать.

 

Команда /help

 

Далее у нас идёт команда /help, которая нужна для помощи с командами. В данном случае /help не очень хорошо написан. Главное что тут нужно исправить это то что туда нужно поместить не названия кнопок, а команд. Тогда на них можно будет нажать, а в моём случае /help имеет лишь информационное назначение.

 

Команда /info

 

Следующая команда /info имеет назначение то же что и /start только без приветствия пользователя. По ней в принципе всё понятно это информация о боте.

 

Меню команд

 

Так же у нас есть удобное меню для команд снизу с кратким описанием каждой команды. Давайте перейдём в /menu магазина из меню команд.

 

Меню магазина /menu

 

Мы с вами видим кнопочное меню магазина. Как сказал Иван в ревью - в меню магазина у меня много лишних команд, обосновав тем, что пользователю не нужны всякие технические команды, для того чтоб пользоваться магазином и мне посоветовали убрать все кнопки кроме categories. Так мы уберём лишнюю логику и не будем загружать пользователя лишней информацией в меню.

Нажмём в кнопочном меню на categories что бы перейти в категории товаров.

 

Меню категорий товаров

 

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

Нажав на кнопку категорий мы попадаем в кнопочное меню всех категорий. Название у категорий странное так как это фейковые названия, которые я сгенерировал библиотекой Faker. Данная библиотека нужна для тестирования функционала чтоб не писать заполнение руками. Это экономит уйму времени.

То что нужно будет написать обязательно - это пагинация. Даже 25 категорий выглядит громоздко, а когда их будет несколько сотен без пагинации будет очень туго. Поэтому в следующей версии обязательно будет пагинация. Давайте перейдём в продукты одной из категорий.

 

Меню продукта

 

Я специально сделал так чтоб товарами заполнялись не все категории, поэтому в одной из категорий мы увидели сообщение, что продукты не найдены.

Если пользователь не администратор, то он не должен будет видеть пустые категории вообще.

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

Перейдём в один из продуктов.

 

Перейдя в продукт мы видим его название, описание и стоимость в Telegram Stars.

У меня показан счёт одна звезда для теста так как у меня всего 49 звёзд на счёте. Поэтому каждый товар в тестировании будет стоить 1 Star.

В product версии я это уберу.

Сейчас давайте попробуем купить продукт.

 

Покупка продукта

 

Нас встречает подтверждение покупки. Мы видим баланс и кнопку подтверждения. Нажмём на неё для подтверждения покупки.

 

Мы видим что мы успешно заплатили 1 Star за файл, поздравление об успешной покупке и возврат средств так как покупка тестовая.

Так же у нас появился файл с названием as.html
Это файл тоже сгенерирован рандомно с расширением html. Давайте его скачаем и посмотрим что у него внутри.

 

Сохранив файл откроем его на ПК.

 

Внутри мы просто видим его название.

Ещё мы посмотрим последнюю команду под названием /paysupport.

 

Команда /paysupport

 

В данной команде мы просто видим информацию о возврате средств.

 

Итог разбора

На этом обзор моего бота "первой версии" подошёл к концу.

Мы с вами увидели что у бота уже довольно много функций, которые нужно улучшать и модифицировать. Поэтому я и называю эту версию первой в кавычках. Пока это недоделанный до конца сырой прототип, который в коде я перепишу до неузнаваемости. В product ему пока ещё рановато так как без пагинации им будет тяжело пользоваться.

В данный момент я уже многое исправил и сейчас добавляю новую функциональность, которая нужна для ТЗ второй версии.

Потом будет ревью второй версии, после которой мне скорее всего придётся многое улучшать и переписывать. Я думаю что самое интересное с этим ботом нас ждёт впереди =)

 

Анонс на следующие статьи

Моя следующая статья выйдет 5.12.24 и будет посвящена рубрике "Cравнение С/C++ и Python". В ней я буду улучшать код телефонных книг, отлавливать ошибки, встраивать аннотации.

Третья статья "Вхожу в IT - telegram bot" скорее всего задержится и не выйдет в конце декабря. Сейчас я разбираю то как работать с хранилищем файлов S3. 
Второго код ревью у меня пока не было, так как бот ещё не закончен до второй версии. Поэтому скорее всего я выпущу несколько статей подряд по рубрике "Cравнение С/C++ и Python".

Дальше будет ещё интереснее :)

 

Заключение

  1. Поговорили об улучшении кода;
  2. Узнали чем полезно менторство и как проводится код ревью;
  3. Узнали о функционале "первой версии" моего бота магазина;
  4. Разобрали пример плохого кода;
  5. Узнали о структуре проекта;

 

Ссылки к статье

  1. Инструмент для управлениями версий Python и виртуальных окружений pyenv
  2. Docker для Начинающих - Полный Курс
  3. Статьи Docker Код на салфетке

Автор

    Нет комментариев

    Реклама