Личный опыт: как прокачать навыки программирования, работая над pet-проектом
История развития одного pet-проекта с полным рефакторингом кода, спамом в деканат и крупной идеей.
17К открытий18К показов
Всем привет! Я студент первого курса технического университета, по воле случая стал старостой группы и погряз в бесконечных отчётах. Отчёты нужно было отправлять каждый день, часто даже без содержимого, а перспектива остаться без премии из-за пропуска одного дня слегка напрягала.
Но, к счастью, у меня был опыт программирования на Python.
Первые шаги
Сначала было необходимо освободиться от отчётов, поэтому я быстренько создал нового бота у «отца ботов» в Telegram и отправился в интернет. Нужно было реализовать следующие пункты:
- Создать простой код для прослушивания сообщений ботом.
- Реализовать изменение даты в нужных ячейках Excel-файла с отчётом.
- Найти способ отправлять сообщения по почте прямо из бота.
Для первого пункта подошла библиотека telebot.
Для второго пункта — openpyxl, datetime.
А для третьего — email, smtplib, mimetypes и много чужого кода.
Запуск и автоматизация
Для автоматической отправки отчётов в деканат было решено использовать библиотеку schedule. Она позволила настроить конкретные дни и время:
Новая проблема не заставила себя долго ждать. Бот был полностью готов к запуску, но на чём его запускать? Тут и пригодилась купленная два года назад Raspberry Pi 3. Для начала хватило простого переноса через флешку всего написанного кода. Но позже пришлось выкручиваться.
Оказалось очень удобно переносить файлы на Raspberry через SSH-соединение. Один запуск bat-файла — и версия бота обновлена.
Также были bat- файлы для старта (./runbot — еще один файл с запуском main.py):
и остановки ботов (к сожалению, останавливает все .py файлы):
Bышeyпoмянyтый файл runbot запускался при каждом перезапуске Raspberry, что позволяло перезапускать бота разными способами.
Однажды в отпуске, уверенный в коде, я залил последнее обновление на Raspberry и ушёл заниматься своими делами. Через несколько дней заметил, что бот отправляет не по одному отчёту, а сразу по 5-10, спамя прямо в деканат. Кажется, всё обошлось ?
Дома я бываю нечасто, так что вскоре появилась необходимость загружать обновление бота через любую точку доступа. В этом помог сервис No-IP. Он позволил обращаться к Raspberry через постоянный IP. После изменения адреса подключения с pi@111.111.1.11
на pi@myconnector.ddns
можно загружать обновлённые файлы из любого точки земли с Wi-Fi.
Предвещая вопрос о том, почему не воспользоваться GitHub для этой задачи, отвечаю — я о нём даже не подумал. А когда всё-таки додумался, система уже работала и необходимости в изменениях не было.
Понеслась: реализация самых разных идей
Из-за проделанной работы захотелось вложить в этот проект ещё что-то полезное и нужное. Я перебирал идею за идеей, реализуя каждую, даже самую странную. Вскоре были добавлены следующие возможности:
- Получение информации о выбранных криптовалютах на бирже Binance.
- Получение данных о времени жизни бота (подробнее об этом в следующем разделе).
- Ввод строки кода Python для расчётов чего-либо.
- Получение информации с Яндекс.Погоды.
- Отправление отчёта вручную.
- Изменение настроек, в которых хранилась информация о городе пользователя, выбранных криптовалютах, имени файла отчёта и т.д.
Но всё это было куда-то не туда.
Рефакторинг кода
Бот часто падал, из-за этого я пропускал сразу несколько дней отправки отчётов. Всё исправить помогла библиотека aiogram. В коде обошёлся минимальными изменениями, но он был просто ужасным. Нагромождая новую функциональность, я забывал о качестве.
Был реализован один глобальный цикл while True, внутри которого находились все handler’ы. Нужно было менять много кода. Помню, тогда я закрылся на несколько часов, изменяя уже почти легаси-код.
В итоге весь код был разбит на отдельные асинхронные handler’ы. После всех этих изменений бот перестал падать.
Вскоре я устроился на свою первую работу программистом. Времени на проект стало сильно не хватать. Только прочувствовав всю силу логирования, решил добавить его в свой проект. С библиотекой logging организовать это было легко.
В проекте быстро стали накапливаться новые идеи, ждущие реализации, появился целый блок из TODO. Однако многие пункты и не реализованы.
Переделка бота под рабочие нужды
На работе было нужно считать отработанные часы. Я студент, работаю удалённо с гибким графиком, грубо говоря, нужно отработать только конкретное количество часов в неделю. Записывать их на бумажку или отправлять себе в сообщения было неудобно. Так появилась идея реализовать подобную функциональность в своём боте.
Основную часть логики я прописал в первые же выходные (тогда я просидел за кодом часов 20-25).
Появилась возможность нажатием одной кнопки “начинать работать”, а нажатием другой кнопки — “заканчивать”. Интервал между двумя нажатиями бот автоматически сохранял в базу данных через PostgreSQL, прикрепляя запись к id конкретного пользователя.
Со временем я добавил вывод данных за прошедшую неделю/месяц, разбиение интервалов работы на теги (теги можно создавать, написав #, а затем сам тег), разделение рабочего и учебного процессов. Старый функционал был спрятан за ненадобностью. А новый расширен.
Немного об Inline-кнопках
После написания основной части этой статьи захотелось протестировать возможности Inline-кнопок. В качестве первого теста я решил добавить конструктор по созданию отработанного периода. Здесь Inline-кнопки оказались незаменимы.
Но для начала немного о том, как их реализовать.
Для своего проекта я создал новый класс-хранилище CallbackItems, в котором есть только один метод __init__. В нём я создал много объектов CallbackData и InlineKeyboardButton, чтобы можно было использовать фильтры в handler’ах и собирать из нужных кнопок Inline-сообщение для каждого пользователя.
Дальше можно создавать handler’ы.
Объекты класса CallbackData позволяют нам использовать фильтр в query_handler’ах.
Помимо использования фильтров, класс CallbackData позволяет получать данные о нажатии кнопки в виде словаря (в коде выше – callback_data: dict).
Где ключ – указанные при создании CallbackData значения (
CallbackData(“date“, “time_unit“, “val“)).
А значение – значение конкретной кнопки, указанное в callback_data, при ее создании (InlineKeyboardButton(
text=”Back”,
callback_data=date_callback.new(
time_unit=“Back”,
val=-1
)))
После нажатия пользователем на Inline-кнопку бот может изменить сообщение. Для этого нужно только добавить строчку кода в handler
Результат добавления Inline-кнопок в код
В итоге Inline-кнопки оказались настолько удобными, что я их добавил везде, где только смог. После нажатия на любую кнопку клавиатуры (кроме Start/Stop working), бот отправлял сообщение с Inline-кнопками.
Конструктор даты (при создании отработанного периода) стал выглядеть так:
Выбор дня отработанного периода так:
Выбор часа начала отработанного периода так:
Генерация отчетов стала в разы удобнее за счёт возможности изменять сообщения после нажатия на Inline кнопку.
Тут я нажал кнопку This week:
А тут This week (details):
При нажатии на разные кнопки, в одном и том же сообщении формируются разные отчёты.
Кроме того, с помощью Inline-кнопок удобно выбирать нужный тег:
Это не все применения Inline-кнопок в моём боте. Остальные вы можете посмотреть в самом боте по ссылке внизу статьи.
Используйте Inline-кнопки, это очень удобно как для вас, так и для пользователя.
Заключение
Так с помощью pet-проекта я познакомился со множеством новых для меня библиотек, получил первую критическую ошибку на проде (теперь можно смеяться над мемами), автоматизировал работу с отчётами и подсчёт отработанного времени.
Чтобы посмотреть, как бот работает сейчас, переходите по ссылке.
Исходный код бота в моём GitHub.
А как вам pet-проекты помогали прокачивать свои навыки?
17К открытий18К показов