Основы Python: создаём телеграм-бота

Практический курс для новичков: вы освоите базовый Python и примените его для создания простого телеграм-бота. Разберёмся с настройкой окружения, обработкой сообщений, командами и запуском бота на постоянной основе.

1. Подготовка: Python, IDE, виртуальное окружение и зависимости

Подготовка: Python, IDE, виртуальное окружение и зависимости

Чтобы спокойно писать код и запускать телеграм-бота, нужно один раз правильно настроить рабочее место: установить Python, выбрать IDE, создать виртуальное окружение и научиться ставить зависимости.

1) Установка Python

Какая версия нужна

Используйте Python 3 (актуальную стабильную версию). Важно: Python 2 давно не поддерживается.

Проверка установки

Откройте терминал (PowerShell/Terminal/bash) и выполните:

Если команда не находится, попробуйте:

Дополнительно проверьте pip (менеджер пакетов):

На Windows иногда удобнее запускать через Python Launcher:

Типичные проблемы

  • Команда python не найдена — Python не установлен или не добавлен в PATH.
  • Несколько версий Python — IDE/терминал могут использовать не ту версию. Решается выбором интерпретатора в IDE и использованием виртуального окружения.
  • 2) IDE (среда разработки)

    IDE помогает писать код быстрее и безопаснее: подсказки, автоформатирование, поиск ошибок, запуск скриптов.

    Что выбрать

    Подойдут любые варианты, но чаще используют:

  • VS Code — лёгкий, гибкий, много расширений.
  • PyCharm — мощный для Python «из коробки».
  • Что обязательно настроить в IDE

  • Открыть папку проекта (не отдельный файл) — так проще управлять зависимостями и запуском.
  • Выбрать интерпретатор Python — желательно из виртуального окружения (.venv).
  • Включить встроенный терминал — чтобы команды pip, python, pytest и т.п. выполнялись прямо из проекта.
  • 3) Структура проекта

    Минимальная, удобная для старта структура может быть такой:

    Папка .venv хранит установленные пакеты и настройки окружения только для этого проекта.

    4) Виртуальное окружение (venv)

    Зачем оно нужно

    Виртуальное окружение решает проблему конфликтов зависимостей:

  • Разные проекты могут требовать разные версии библиотек.
  • Установка пакетов не «загрязняет» систему.
  • Проект становится воспроизводимым: можно развернуть окружение заново по списку зависимостей.
  • Создание venv

    Перейдите в папку проекта и выполните:

    Если у вас команда python не работает, используйте python3 или на Windows py:

    Активация venv

    Windows (PowerShell):

    Windows (cmd):

    macOS / Linux:

    После активации в терминале обычно появляется префикс, например:

    Выход из окружения

    Быстрая проверка: вы точно в venv?

    Сравните пути:

    В пути должна фигурировать папка .venv.

    5) Зависимости: установка и фиксация версий

    Установка пакетов

    Устанавливайте пакеты внутри активированного venv:

    Если pip вызывает «не тот» Python, безопаснее так:

    Почему важно фиксировать версии

    Если сегодня вы поставили библиотеку, а через месяц она обновилась и стала работать иначе — проект может сломаться. Поэтому зависимости обычно фиксируют.

    Файл requirements.txt

    Это список пакетов и версий. Его можно создать автоматически:

    Установка зависимостей из файла:

    Обновление зависимостей без хаоса

  • Установили новый пакет.
  • Проверили, что всё запускается.
  • Обновили requirements.txt через pip freeze > requirements.txt.
  • 6) Полезные команды для диагностики

    pip show полезен, чтобы увидеть версию, описание и место установки пакета.

    ---

    Задания для закрепления

  • Проверьте, что Python и pip доступны из терминала. Выпишите версии.
  • Создайте папку проекта и виртуальное окружение .venv.
  • Активируйте виртуальное окружение и убедитесь, что sys.executable указывает на .venv.
  • Установите любой пакет (например, requests), затем создайте requirements.txt.
  • Удалите виртуальное окружение (папку .venv), создайте заново и восстановите зависимости из requirements.txt.
  • <details> <summary> Ответы (пример выполнения) </summary>

    1) Проверка версий:

    На Windows возможно:

    2) Создание окружения:

    3) Активация и проверка:

    macOS/Linux

    Windows PowerShell

    В выводе должен быть путь внутри .venv.

    4) Установка пакета и фиксация:

    5) Восстановление:

  • Удалите папку .venv.
  • Создайте заново и активируйте.
  • Установите зависимости:
  • </details>

    2. База Python: типы данных, условия, циклы и функции

    База Python: типы данных, условия, циклы и функции

    Эта база нужна, чтобы уверенно обрабатывать входящие сообщения бота: текст, команды, аргументы, состояния.

    1) Переменные и типы данных

    В Python тип «прикреплён» к значению, а не к имени переменной:

    Числа

  • int — целые: 42, -7
  • float — вещественные: 3.14 (важно для времени/денег: могут быть погрешности)
  • Строки (str)

    Строки — это текст.

    Часто нужно безопасно «собирать» строки:

    Логический тип и пустое значение

  • bool: True / False
  • None: «нет значения» (например, пользователь ещё не прислал нужные данные)
  • Коллекции

    #### Список (list) — изменяемый

    #### Кортеж (tuple) — неизменяемый

    #### Словарь (dict) — ключ → значение Идеален для хранения данных по пользователю.

    #### Множество (set) — уникальные элементы

    Индексация и срезы

    2) Условия: if / elif / else

    Условия управляют логикой: какая команда пришла, есть ли аргументы, заполнены ли данные.

    Сравнения и логика

  • ==, !=, <, <=, >, >=
  • and, or, not
  • «Правдивость» значений (truthy/falsy)

    В условиях некоторые значения считаются ложными: "", 0, [], {}, set(), None.

    3) Циклы: for и while

    Циклы нужны, чтобы пройтись по словам сообщения, по списку команд, по словарю данных.

    for по последовательности

    range и enumerate

    range(n) даёт числа от 0 до n-1.

    Обход словаря

    break и continue

  • break — прервать цикл
  • continue — перейти к следующей итерации
  • while — когда не знаете число шагов заранее

    4) Функции: как упаковывать логику

    Функции помогают не копировать код и делать обработчики сообщений понятнее.

    Определение и return

  • Параметры — входные данные.
  • return — результат работы.
  • Аннотации типов (text: str) необязательны, но улучшают читаемость.
  • Значения по умолчанию

    Область видимости (scope)

    Переменные внутри функции «живут» внутри неё.

    Если нужно сохранить состояние между вызовами, чаще используют структуры данных (например, dict), которые лежат снаружи и передаются в функции.

    Полезный паттерн: разбор команды

    Задания для закрепления

    1) Напишите функцию normalize_text(text), которая: - обрезает пробелы по краям; - приводит к нижнему регистру; - заменяет последовательности пробелов внутри на один пробел.

    2) Напишите функцию count_words(text), которая возвращает словарь частот слов (разделитель — пробел).

    3) Дан список входящих сообщений: Выведите для каждого сообщения: - если это команда /echo, напечатайте всё, что после команды; - если это не команда, напечатайте TEXT: и нормализованный текст.

    <details> <summary> Ответы (пример решения) </summary>

    </details>

    3. Работа со строками и коллекциями для обработки сообщений

    Работа со строками и коллекциями для обработки сообщений

    Когда бот получает сообщение, чаще всего это строка (текст), которую нужно:

  • Нормализовать (убрать лишние пробелы, привести к единому регистру).
  • Понять, команда это или обычный текст.
  • Извлечь аргументы.
  • Разрулить логику с помощью коллекций: списков, словарей, множеств.
  • Базовые конструкции Python (условия, циклы, функции) уже были в предыдущей статье — здесь используем их как инструмент.

    1) Нормализация текста: чтобы сравнения работали стабильно

    Проблема: пользователь может написать " /START hi ", а вы ожидаете /start.

    Практичные операции со строками:

  • strip() — убирает пробелы по краям.
  • split() без аргументов — режет по любым пробельным подряд.
  • " ".join(...) — склеивает обратно одним пробелом.
  • lower() или casefold() — приводит к нижнему регистру (для пользовательского текста casefold() иногда надёжнее для разных алфавитов).
  • Что это даёт:

    2) Разбор команд: отделяем имя команды от аргументов

    У команды обычно есть форма:

    Удобные методы:

  • split(maxsplit=1) — разделить строку на «первый токен» и «всё остальное».
  • partition(" ") — всегда вернёт 3 части: до разделителя, сам разделитель, после.
  • removeprefix() — удобно убрать ведущий / (если уверены, что он есть).
  • Пример «бережного» парсинга:

    Частый нюанс: команды вида /start@BotName

    Telegram иногда присылает команду вместе с именем бота. Минимально можно «отрезать» хвост после @:

    3) Списки для аргументов: чистим, склеиваем, ограничиваем

    Аргументы удобно хранить списком args. Типовые задачи:

  • Склеить обратно в строку:
  • Взять «первый аргумент» безопасно:
  • Ограничить длину (например, не принимать слишком длинные сообщения):
  • Быстрая фильтрация через генератор/списковое включение:
  • 4) Множества (set): быстрые проверки «разрешено/запрещено»

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

    Почему set хорош: проверка in концептуально «быстрая» и код читается как «входит ли команда в список разрешённых».

    5) Словарь (dict) как роутер команд

    Вместо длинной цепочки if/elif удобно сделать таблицу соответствия «команда → функция-обработчик».

    Ключевые приёмы:

  • .get(key) — безопасно достаёт значение (вернёт None, если ключа нет).
  • Словарь становится «конфигурацией» поведения бота.
  • 6) Словарь для данных пользователя: аккуратные обновления

    Часто нужно хранить промежуточные данные (например, настройки) по user_id.

    setdefault удобен тем, что экономит проверку «есть ли пользователь в словаре».

    ---

    Задания для закрепления

    1) Напишите функцию extract_command(text), которая возвращает (cmd, args):

  • нормализует текст;
  • если это не команда — возвращает ("", []);
  • убирает суффикс @BotName у команды.
  • 2) Дан список сообщений:

    С помощью роутера (словаря) обработайте сообщения так:

  • /start"OK"
  • /echo → повторить аргументы
  • неизвестная команда → "ERR"
  • текст → "TEXT"
  • 3) Напишите функцию unique_words(text), которая возвращает множество уникальных слов нормализованного текста.

    <details> <summary> Ответы </summary>

    </details>

    4. Создаём бота: токен, Bot API и первая команда /start

    Создаём бота: токен, Bot API и первая команда /start

    В этой статье вы создадите телеграм-бота, получите токен и напишете минимальный код, который отвечает на команду /start.

    > Важно: установка Python, виртуального окружения и зависимостей уже разобрана в статье про подготовку. Здесь считаем, что вы работаете внутри активированного .venv.

    1) Что такое Bot API и как бот получает сообщения

    Telegram Bot API — это способ взаимодействия вашего кода с Telegram-серверами.

    Есть два базовых режима получения сообщений:

  • Long polling (мы используем его): ваш код «долго» спрашивает сервер: «есть ли новые обновления?». Это просто для старта: запустили скрипт — бот работает.
  • Webhook: Telegram сам отправляет обновления на ваш сервер по HTTPS. Это требует публичного адреса и настройки сервера.
  • В режиме polling ваш процесс должен постоянно работать. Если вы закроете терминал — бот перестанет отвечать.

    2) Получаем токен через BotFather

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

    Сделайте так:

  • В Telegram найдите BotFather.
  • Отправьте команду /newbot.
  • Задайте имя и username (username должен заканчиваться на bot).
  • BotFather выдаст строку вида:
  • Это и есть токен.

    Правила безопасности токена

  • Не публикуйте токен в чате, скриншотах и репозиториях.
  • Не «хардкодьте» токен в коде.
  • Если токен утёк — сразу перевыпустите его у BotFather командой /revoke.
  • 3) Храним токен в переменной окружения (через .env)

    Самый удобный вариант для учебного проекта — файл .env, который не коммитят.

    Создайте файл .env в корне проекта:

    И добавьте .env в .gitignore, если используете git:

    Чтобы читать .env, установим маленькую зависимость. В активированном окружении:

    Зафиксируйте зависимости в requirements.txt так, как вы делали в статье про зависимости.

    4) Минимальный бот на aiogram: команда /start

    Создайте файл src/main.py:

    Что здесь происходит:

  • load_dotenv() переносит значения из .env в переменные окружения.
  • Bot(token=token) создаёт объект бота с вашим секретным ключом.
  • Dispatcher() принимает входящие обновления.
  • Декоратор @dp.message(CommandStart()) регистрирует обработчик команды /start.
  • start_polling() запускает бесконечный цикл получения сообщений.
  • 5) Запуск и быстрая проверка

    Запустите из корня проекта:

    Проверка:

  • Откройте чат с вашим ботом.
  • Нажмите Start или отправьте /start.
  • Бот должен ответить приветствием.
  • Остановить бота можно Ctrl + C в терминале.

    Частые проблемы

  • Бот молчит: убедитесь, что запущен ваш скрипт и вы пишете именно своему боту (проверьте username).
  • Ошибка токена: проверьте, что BOT_TOKEN загружен (файл .env в корне, без лишних кавычек и пробелов).
  • Зависимости не находятся: проверьте, что запускаете Python из вашего .venv (это проверяли в статье про окружение).
  • ---

    Задания для закрепления

    1) Измените ответ на /start, чтобы бот писал:

  • Ваш user_id: ...
  • Ваше имя: ...
  • 2) Добавьте команду /ping, которая отвечает pong.

    3) Добавьте обработчик для любого текста (не команды), который отвечает: Вы написали: <текст>.

    <details> <summary> Ответы (пример решения) </summary>

    </details>

    5. Хендлеры: ответы на текст, команды и простая логика диалога

    Хендлеры: ответы на текст, команды и простая логика диалога

    Хендлер (handler) — это функция, которую aiogram вызывает, когда входящее обновление (обычно сообщение) подходит под условия (фильтры). В прошлой статье мы уже запустили бота и сделали /start. Теперь разберём, как:

  • разделять обработку команд и обычного текста;
  • делать “запасные” ответы (неизвестная команда, пустой текст);
  • собрать простую логику диалога без сложных FSM.
  • 1) Базовая форма хендлера

    Хендлер в aiogram v3 — это async def, который принимает Message и делает ответ через await message.answer(...).

    Важно помнить про входной текст:

  • message.text может быть None (например, если прислали стикер/фото).
  • Команды в Telegram — тоже текст, который начинается с /.
  • Минимальный “безопасный” доступ:

    2) Фильтры: как повесить разные реакции

    Вместо ручного парсинга строки, в aiogram удобно использовать фильтры.

    Команды

  • CommandStart() — только /start.
  • Command("ping") — конкретная команда /ping.
  • Текст (не команда)

    Для текста часто применяют фильтр по полям через F:

    Если вы хотите отделить команды от обычного текста, добавьте проверку:

    3) Порядок хендлеров и “fallback”

    Практическое правило: более конкретные обработчики объявляйте выше, а “универсальные” — ниже.

    Типовой набор:

  • конкретные команды (/start, /help, /ping)
  • специфичные сценарии диалога (например, “ждём имя”)
  • обработка обычного текста
  • обработка неизвестных команд
  • обработка не-текста (стикеры, фото)
  • Пример “неизвестной команды”:

    И обработка “не текста”:

    4) Простая логика диалога: состояние в словаре

    Иногда нужно задать вопрос и ожидать ответ следующим сообщением, например:

    Без сложных машин состояний можно сделать так:

  • user_state[user_id] хранит, что мы “ждём” от пользователя
  • profiles[user_id] хранит данные (имя, город и т.п.)
  • Пример кода:

    Ключевые идеи:

  • Состояние должно очищаться (например, после успешного ввода или через /cancel).
  • Проверяйте вход (пустая строка, слишком длинный текст и т.п.).
  • Этот подход учебный: после перезапуска процесса память исчезнет — это нормально на старте.
  • ---

    Задания для закрепления

    1) Добавьте команду /help, которая выводит список команд: /start, /help, /ping, /setname, /cancel.

    2) Сделайте диалог /setcity по аналогии с /setname:

  • /setcity → бот спрашивает город;
  • следующий текст сохраняется как city.
  • 3) Реализуйте команду /me, которая показывает сохранённые данные пользователя (имя и город). Если чего-то нет — выводите не задано.

    <details> <summary> Ответы (пример реализации) </summary>

    </details>

    6. Клавиатуры и кнопки: Reply и Inline для удобства пользователя

    Клавиатуры и кнопки: Reply и Inline для удобства пользователя

    Клавиатуры в Telegram решают две практичные задачи:

  • Пользователю проще нажать кнопку, чем вводить команду вручную.
  • Вы уменьшаете число “неправильных” сообщений и упрощаете логику бота.
  • В Telegram есть два основных типа кнопок:

  • Reply-клавиатура — “панель кнопок” вместо системной клавиатуры. При нажатии кнопка отправляет обычное сообщение (текст/контакт/локацию).
  • Inline-клавиатура — кнопки “прикреплены” к конкретному сообщению. При нажатии обычно приходит callback-событие (или открывается ссылка).
  • > В предыдущей статье мы уже настраивали хендлеры и простое состояние в словаре. Здесь добавим кнопки, не углубляясь в общий роутинг.

    ---

    1) Reply-клавиатура (ReplyKeyboardMarkup)

    Когда использовать

  • Вы хотите дать пользователю “меню” из 2–6 вариантов.
  • Ответ — это текст, который удобно обработать как обычное сообщение.
  • Вам подходят “крупные кнопки” под полем ввода.
  • Минус: пользователь может всё равно написать что угодно вручную, поэтому валидация остаётся.

    Пример: меню команд и кнопка «Отмена»

    adjust(2) — раскладка в 2 кнопки в ряд.

    Как “убрать” Reply-клавиатуру

    Когда меню больше не нужно (например, диалог завершён), лучше убрать клавиатуру:

    ---

    2) Inline-клавиатура (InlineKeyboardMarkup)

    Inline-кнопки живут “под сообщением” и подходят, когда выбор относится именно к этому сообщению: подтвердить действие, выбрать вариант, пролистать список.

    Два частых вида inline-кнопок

  • URL-кнопка — открывает ссылку (никаких хендлеров в боте не требуется).
  • Callback-кнопка — отправляет боту CallbackQuery, вы обрабатываете событие.
  • Пример: кнопки «👍/👎» и обработка callback

    Ключевые моменты:

  • callback.data — строка, которую вы сами задали в callback_data.
  • await callback.answer() — техническое подтверждение нажатия.
  • callback.message — исходное сообщение, к которому были прикреплены кнопки.
  • Обновление сообщения вместо нового ответа

    Иногда лучше не плодить новые сообщения, а “перерисовать” текущее:

    ---

    3) Как проектировать callback_data, чтобы не запутаться

    callback_data — это ваш мини-протокол. Держите его простым:

  • Префикс действия: "city:", "rate:", "page:".
  • Один разделитель (например, :) и понятные значения.
  • Не кладите секреты (токены, пароли). Пользователь технически может увидеть/подделать данные.
  • Пример шаблонов:

    Если данных становится много, лучше перейти на фабрики callback-данных (но для учебного бота обычно достаточно строк).

    ---

    4) Что выбрать: Reply или Inline

    | Задача | Лучше подходит | |---|---| | Меню команд, кнопка “Отмена”, ввод текстом | Reply | | Подтвердить действие под конкретным сообщением | Inline | | Лайк/дизлайк, выбор варианта, пагинация | Inline | | Нужно отправить контакт/локацию кнопкой | Reply |

    Практика: часто используют оба типа. Reply — как “главное меню”, Inline — как “действия здесь и сейчас”.

    ---

    Задания для закрепления

    1) Добавьте команду /menu с Reply-клавиатурой: /help, /me, /setname, /setcity, /cancel.

    2) Добавьте команду /confirm, которая отправляет сообщение с inline-кнопками:

  • Да с callback_data="confirm:yes"
  • Нет с callback_data="confirm:no"
  • Обработайте callback так, чтобы бот отвечал: Вы выбрали: Да/Нет.

    3) Улучшение UX: после выбора Да/Нет отредактируйте исходное сообщение на Спасибо, выбор принят. и уберите кнопки.

    <details> <summary> Ответы (пример реализации) </summary>

    </details>

    7. Запуск и поддержка: логирование, ошибки и деплой на сервер

    Запуск и поддержка: логирование, ошибки и деплой на сервер

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

    > Про токен и .env, виртуальное окружение и зависимости см. статьи про подготовку и первый запуск бота — здесь не повторяем, а используем.

    ---

    1) Логирование: чтобы видеть, что делает бот

    print() помогает на старте, но для поддержки нужен logging:

  • Можно выставлять уровни (INFO, WARNING, ERROR).
  • Логи можно писать в файл.
  • Логи удобно читать при падениях и на сервере.
  • Минимальная настройка

    Добавьте в src/main.py настройку логов (лучше в начале main()):

    Что означают части формата:

  • %(asctime)s — время события.
  • %(levelname)s — уровень важности.
  • %(name)s — имя логгера (обычно модуль).
  • %(message)s — ваше сообщение.
  • Использование:

    Логирование входящих сообщений (аккуратно)

    Логи — это почти “данные пользователя”. Не пишите туда токены и лишний персональный текст. Практичный компромисс:

    ---

    2) Ошибки: как не уронить бота из‑за одного апдейта

    В polling-боте критично: одно сообщение не должно валить весь процесс.

    Базовая защита внутри хендлеров

    Если вы делаете потенциально “ломкую” операцию (парсинг, обращение к словарям профилей, конвертации), ловите ожидаемые ошибки и отвечайте пользователю понятным текстом.

    Идея простая:

  • Пользователь получает “мягкую” ошибку.
  • Вы получаете traceback в логах.
  • Глобальный “аварийный” хендлер для апдейтов

    В aiogram удобно иметь общий перехватчик ошибок на случай, если что-то забыли обернуть.

    Так вы фиксируете проблему, но бот продолжает жить.

    ---

    3) Корректный запуск/остановка

    На практике бот останавливают сигналом (например, при перезапуске сервиса). Важно:

  • Логи должны успеть записаться.
  • Процесс должен завершиться без “висящих” задач.
  • Минимальная рекомендация: держите запуск через asyncio.run(main()), как в предыдущей статье, и не создавайте “вечных” фоновых задач без контроля.

    ---

    4) Деплой на сервер: минимальный рабочий рецепт

    Ниже — путь “без магии”: Linux-сервер (VPS), запуск как сервис.

    Шаги на сервере (кратко)

  • Скопируйте проект на сервер (git/архив — как вам удобнее).
  • Создайте виртуальное окружение и установите зависимости из requirements.txt.
  • Добавьте .env на сервере (не коммитьте его).
  • Запустите бота под менеджером процессов.
  • Проверка ручного запуска (перед автоматизацией):

    Если в логах видно “started” и бот отвечает — можно фиксировать как сервис.

    Запуск через systemd (рекомендуемый минимум)

    Создайте unit-файл (пример):

    Пояснения по ключевым строкам:

  • User — отдельный пользователь без лишних прав.
  • WorkingDirectory — чтобы относительные пути работали ожидаемо.
  • EnvironmentFile — подхват переменных (BOT_TOKEN и др.).
  • ExecStart — точный python из вашего .venv.
  • Restart=always — сервис перезапустится при падении.
  • Команды управления сервисом:

    Просмотр логов сервиса:

    ---

    5) Типовые проблемы в проде (и быстрые проверки)

  • Сервис стартует, но бот “молчит”: проверьте BOT_TOKEN (сервис читает нужный .env?), и что запущен именно тот src/main.py.
  • ModuleNotFoundError: почти всегда запускается “системный” python, а не .venv. Проверьте ExecStart.
  • Спам ошибками: добавьте валидации входа (проверки message.text, длины текста) и логируйте “аномалии” уровнем WARNING, а падения — ERROR/exception.
  • ---

    Задания для закрепления

    1) Добавьте логирование:

  • Лог “bot started” при запуске.
  • Лог входящих сообщений с user_id и content_type.
  • 2) Сделайте так, чтобы при любой неожиданной ошибке в хендлере:

  • В лог писался traceback.
  • Пользователь получал сообщение “Произошла ошибка”.
  • 3) Подготовьте unit-файл systemd для вашего проекта (пути можно указать шаблонно) и опишите 3 команды для: старт, статус, просмотр логов.

    <details> <summary> Ответы (пример) </summary>

    1) Логирование старта и входящих сообщений:

    2) Защита хендлера:

    (Дополнительно можно повесить глобальный перехватчик ошибок)

    3) systemd unit (шаблон):

    Команды:

    </details>