Давно уже было желание сделать что-то простое и полезное в Telegram, но чтобы не пришлось постоянно за этим следить. И вот пришла гениальная идея: чтобы быть всегда замотивированным, можно написать бота, который будет (с какой то периодичностью) сам отправлять мотивационные цитаты в канал?

Этот бот берёт случайные цитаты известных людей из интернета, переводит их на русский язык и отправляет в Telegram-канал по расписанию. Например, утром, днём, вечером и ночью. Звучит просто, правда? Но внутри этого проекта есть всё, что нужно для обучения: чистая архитектура, работа с API, планировщик задач и даже деплой на облачную платформу Railway.

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

Что в итоге получилось и код проекта можно найти по этим ссылкам.

1. Архитектура проекта

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

Я выделил четыре основных слоя:

  1. Entities (Сущности):

    • Это базовые структуры данных, например, Quote (цитата). Они описывают, как выглядят данные в программе.

    • Пример: цитата состоит из текста и автора.

  2. Use Cases (Бизнес-логика):

    • Здесь находится "сердце" приложения — логика работы с данными.

    • Например, получение цитаты, её перевод и отправка в Telegram.

  3. Interfaces (Интерфейсы):

    • Это абстракции для взаимодействия с внешними системами. Например, интерфейс для работы с API цитат или Telegram Bot API.

    • Интерфейсы позволяют легко менять реализацию без изменения остального кода.

  4. Adapters (Адаптеры):

    • Реализация интерфейсов. Например, адаптер для ZenQuotes API или Telegram Bot API.

    • Эти модули "общаются" с внешним миром, но скрывают детали от остальной программы.

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

2.Создание Telegram-бота

Чтобы создать Telegram-бота, выполните следующие шаги:

Откройте Telegram и найдите бота @BotFather .

Начните диалог с ним и используйте команду /newbot, чтобы создать нового бота.

Следуйте инструкциям:

  1. Придумайте имя бота (например, MotivatorBot).

  2. Укажите username бота (например, motivator_bot).

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

Этот токен понадобится для взаимодействия с Telegram Bot API.

Важно: Не делитесь токеном с другими людьми и не публикуйте его в открытом доступе!

3.Создание Telegram-канала

  1. Откройте Telegram и нажмите на значок меню (три полоски).

  2. Выберите "Новый канал".

  3. Укажите название канала (например, "Мотиватор") и добавьте описание.

  4. Установите тип канала: "Публичный" или "Приватный".

Чтобы бот мог отправлять сообщения в канал, его нужно сделать администратором:

  1. Откройте настройки канала.

  2. Перейдите в раздел "Администраторы".

  3. Нажмите "Добавить администратора".

  4. Найдите вашего бота по имени (например, motivator_bot) и добавьте его.

3. Как получить CHAT_ID

CHAT_ID — это уникальный идентификатор канала, который нужен для отправки сообщений.

  1. Добавьте бота @userinfobot в свой Telegram.

  2. Отправьте ему любое сообщение из созданного канала.

  3. Бот ответит информацией о chat_id вашего канала. Для каналов этот ID обычно начинается с -100.

Пример CHAT_ID:

-1001234567890

Важно: Сохраните CHAT_ID — он понадобится для настройки бота.

Итого

Теперь у вас есть всё необходимое для начала разработки:

  • Создан Telegram-бот и получен его токен.

  • Создан канал, добавлен бот в администраторы и получен CHAT_ID.

В следующем разделе мы перейдём к реализации логики бота.

4. Разработка бота

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

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

telegram-quotes-bot/
├── go.mod                  # Файл для управления зависимостями Go Modules.
├── go.sum                  # Список контрольных сумм для зависимостей.
├── cmd/                    # Директория с точкой входа в приложение.
│   └── main.go             # Главный файл, где запускается бот.
├── internal/               # Внутренние пакеты проекта.
│   ├── config/             # Конфигурация приложения.
│   │   └── config.go       # Загрузка переменных окружения.
│   ├── adapters/           # Адаптеры для работы с внешними сервисами.
│   │   ├── telegram_adapter.go  # Адаптер для Telegram Bot API.
│   │   ├── zenquotes.go    # Адаптер для ZenQuotes API (получение цитат).
│   │   └── mymemory.go     # Адаптер для MyMemory API (перевод текста).
│   ├── usecases/           # Бизнес-логика приложения.
│   │   ├── fetch_quote.go  # Получение цитаты.
│   │   ├── translate.go    # Перевод текста.
│   │   └── send_quote.go   # Отправка цитаты в Telegram.
│   └── interfaces/         # Интерфейсы для ��заимодействия с внешними системами.
│       ├── api.go          # Общие интерфейсы для API.      
└── Dockerfile              # Файл для сборки Docker-образа.
  1. cmd/main.go: Это точка входа в программу. Здесь инициализируется всё приложение: загружаются конфигурации, создаются адаптеры и запускается планировщик Cron.

  2. internal/config: Здесь находится код для работы с переменными окружениями. Это позволяет легко управлять настройками проекта.

  3. internal/adapters: Адаптеры — это модули, которые "общаются" с внешними сервисами (например, Telegram, ZenQuotes, MyMemory). Если что-то изменится в API, нужно будет править только этот слой.

  4. internal/usecases: Здесь находится бизнес-логика приложения. Например, получение цитаты, её перевод и отправка в Telegram.

  5. internal/interfaces: Интерфейсы описывают, как должны работать адаптеры. Это делает код более гибким: можно заменить реализацию адаптера, не трогая остальной код.

  6. Dockerfile: Этот файл нужен для сборки Docker-образа, чтобы запустить бота на облачной платформе, например, Railway.

Такая организация помогает держать код в порядке, особенно если проект будет расти или меняться.

4.2. Бизнес-логика

Бизнес-логика — это сердце приложения, где происходят все основные действия: получение цитат, их перевод и отправка в Telegram.

Получение цитат

Чтобы получить случайную цитату, бот отправляет HTTP-запрос к ZenQuotes API. Вот как это работает:

Ссылка для запроса:

https://zenquotes.io/api/random

Этот запрос возвращает JSON с одной случайной цитатой.

Пример ответа:

[
  {
    "q": "The best way to predict the future is to create it.",
    "a": "Peter Drucker"
  }
]

Обработка ответа:

  • Бот извлекает текст цитаты (q) и имя автора (a).

  • Если что-то пошло не так (например, API недоступен), бот логирует ошибку и пробует снова.

Перевод текста

Цитаты на английском нужно перевести на русский язык. Для этого используется MyMemory API.

Ссылка для запроса:

https://api.mymemory.translated.net/get?q=текст&langpair=en|ru

Здесь текст — это текст цитаты, который нужно перевести.

Пример ответа:

{
  "responseData": {
    "translatedText": "Одна ошибка и ты ошибся"
  }
}

Обработка ошибок перевода:

  • Если API вернул пустой перевод или произошла ошибка, бот отправляет оригинальный текст цитаты.

  • Логируются все проблемы, чтобы можно было разобраться с ними позже.

Отправка сообщений

После того как цитата переведена, её нужно отправить в Telegram-канал. Для этого используется Telegram Bot API.

Форматирование сообщений с эмодзи:

Чтобы сообщения выглядели красиво, я добавил эмодзи:

📖 Одна ошибка и ты ошибся.

— Сократ ✍️

Отправка сообщения:

Бот использует метод sendMessage Telegram Bot API.

Пример запроса:

{
  "chat_id": "-1001234567890",
  "text": "📖 Одна ошибка и ты ошибся.\n\n— Сократ ✍️"
}

Обработка ошибок:

  • Если сообщение не отправилось (например, из-за проблем с интернетом), бот повторяет попытку.

  • Все ошибки логируются, чтобы можно было быстро найти проблему.

Как это всё работает вместе?

  1. Бот запрашивает случайную цитату у ZenQuotes API.

  2. Полученный текст отправляется в MyMemory API для перевода на русский язык.

  3. Переведённая цитата форматируется с эмодзи и отправляется в Telegram-канал через Telegram Bot API.

  4. Если что-то пошло не так (например, API недоступен), бот обрабатывает ошибку и продолжает работу.

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

4.3. Планировщик задач

Чтобы бот отправлял цитаты в Telegram-канал по расписанию, я использовал планировщик задач Cron. Этот инструмент позволяет запускать определённые действия в заданное время. Разберём, как настроить Cron и как логировать его работу.

Настройка Cron

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

Формат Cron-выражения:

┌──────────── минуты (0–59)
│ ┌────────── часы (0–23)
│ │ ┌──────── день месяца (1–31)
│ │ │ ┌────── месяц (1–12)
│ │ │ │ ┌──── день недели (0–6, где 0 — воскресенье)
│ │ │ │ │
* * * * *

Пример Cron-выражения для нашего бота:

Если мы хотим отправлять цитаты в 8:00, 12:00, 18:00 и 22:00 UTC, выражение будет выглядеть так:

0 8,12,18,22 * * *
  • 0: Минута (в начале часа).

  • 8,12,18,22: Часы (8:00, 12:00, 18:00, 22:00 UTC).

  • * : Любой день месяца.

  • * : Любой месяц.

  • *: Любой день недели.

Если вы хотите, чтобы бот отправлял цитаты каждые 30 минут, используйте следующее Cron-выражение:

*/30 * * * *

Как перевести время в UTC? Если ваше время отличается от UTC (например, самарское время опережает UTC на 4 часа), нужно перевести расписание:

  • 8:00 SAMT → 4:00 UTC

  • 12:00 SAMT → 8:00 UTC

  • 18:00 SAMT → 14:00 UTC

  • 22:00 SAMT → 18:00 UTC

Тогда Cron-выражение для самарского времени будет:

0 4,8,14,18 * * *

Чтобы следить за работой планировщика, я добавил логирование всех ключевых шагов: при запуске задачи бот записывает в лог сообщение [INFO] Задача отправки цитаты запущена, при ошибках (например, недоступность API или сбой отправки) фиксируется проблема в формате [ERROR] Ошибка при получении цитаты: таймаут запроса, а при успешной отправке цитаты логируется информация вида [INFO] Цитата успешно отправлена: "Одна ошибка и ты ошибся.". Для удобства анализа я использую библиотеку log/slog из стандартной библиотеки Go, которая позволяет форматировать логи в JSON.

Как это работает вместе?

  1. Cron запускает задачу отправки цитаты в указанное время.

  2. Бот выполняет все шаги: получает цитату, переводит её и отправляет в Telegram.

  3. Все действия записываются в логи для дальнейшего анализа.

5. Безопасность и переменные окружения

Для безопасной работы бота важно правильно хранить данные, такие как токен Telegram-бота и ID канала. Эти данные не должны попадать в открытый доступ, например, в репозиторий GitHub. Разберём, как организовать их хранение и загрузку.

Хранение данных

Для локальной разработки я использую файл .env, который хранит все переменные окружения. Вот пример такого файла:

BOT_TOKEN=123456789:AAFwz3QxYzZzZzZzZzZzZzZzZzZzZzZz
CHAT_ID=-1001234567890

Файл .env добавляется в .gitignore, чтобы он не попал в репозиторий. Для загрузки переменных из .env используется библиотека github.com/joho/godotenv.

Настройка переменных окружения на Railway

Когда проект развёрнут на Railway, переменные окружения настраиваются через веб-интерфейс.

Безопасная загрузка переменных в код

Чтобы избежать проблем с отсутствием переменных окружения, я добавил проверки и обработку ошибок:

1. Проверка наличия переменных:

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

botToken := os.Getenv("BOT_TOKEN")
chatIDStr := os.Getenv("CHAT_ID")
if botToken == "" || chatIDStr == "" {
    logger.Error("Необходимые переменные окружения отсутствуют", "BOT_TOKEN", botToken, "CHAT_ID", chatIDStr)
    os.Exit(1)
}

2.Обработка ошибок:

Если значение переменной (например, CHAT_ID) не может быть преобразовано в нужный формат, бот также завершает работу с логированием ошибки:

chatID, err := strconv.ParseInt(chatIDStr, 10, 64)
if err != nil {
    logger.Error("Ошибка преобразования CHAT_ID в int64", "error", err)
    os.Exit(1)
}

3.Логирование:

Все действия с переменными окружениями логируются для удобства отладки:

logger.Info("Переменные окружения загружены", "BOT_TOKEN", botToken, "CHAT_ID", chatID)

7. Деплой на Railway

Railway — это облачная платформа для развёртывания приложений, которая позволяет ��ыстро запускать проекты без необходимости настройки серверов. Она поддерживает автоматический деплой из GitHub, что делает процесс максимально простым. Railway идеально подходит для небольших проектов, таких как Telegram-бот, благодаря удобному интерфейсу, бесплатному тарифу и встроенной поддержке Docker.

Шаги деплоя

1. Создание проекта на Railway

  • Зарегистрируйтесь на Railway или войдите в свой аккаунт.

  • Нажмите "New Project" и выберите "Deploy from GitHub Repo".

  • Выберите репозиторий с вашим проектом.

2. Настройка переменных окружения

  • Перейдите в раздел "Variables" вашего проекта на Railway.

  • Добавьте переменные окружения:

BOT_TOKEN=ваш_токен_бота
CHAT_ID=ваш_chat_id

3. Dockerfile для сборки образа

FROM golang:1.20-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o bot ./cmd

FROM alpine:latest
COPY --from=builder /app/bot /bot
CMD ["/bot"]

Railway автоматически найдёт Dockerfile и соберёт образ.

4. Автоматический деплой через GitHub

  • Свяжите Railway с вашим GitHub-репозиторием.

  • Настройте автоматический деплой при каждом обновлении кода.

  • После первого коммита Railway соберёт и запустит проект.

Итоги

Получился Telegram-бота, который автоматически отправляет мотивационные цитаты в канал по расписанию. В процессе разработки мы научились работать с Telegram Bot API для отправки сообщений, использовали внешние API (ZenQuotes и MyMemory) для получения и перевода цитат, реализовали планировщик задач с помощью Cron и освоили развёртывание приложения на облачной платформе Railway. Этот проект помог нам приобрести важные навыки: работу с API, чистую архитектуру, логирование, безопасное хранение данных и деплой приложений. Теперь у вас есть готовый инструмент, который можно дорабатывать и адаптировать под свои нужды, а также база для создания новых Telegram-ботов.

Полезные ссылки