Базовый каркас Telegram-бота на Go
Базовый каркас Telegram-бота на Go. Этот код включает:
- Входную точку (
cmd/bot/main.go
) - Конфигурацию (
config
) - Слой инфраструктуры (бот) (
internal/infrastructure/telegram
) - Сервисный слой (
internal/usecase
) - Доменные сущности (
internal/domain
) - Слой репозитория (если нужно хранить данные) (
internal/repository
)
Структура проекта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
telegram-bot/ │── cmd/ │ └── bot/ │ └── main.go │── config/ │ └── config.go │── internal/ │ ├── domain/ │ │ └── message.go │ ├── infrastructure/ │ │ ├── telegram/ │ │ │ ├── bot.go │ │ ├── database/ │ │ │ ├── postgres.go │ ├── repository/ │ │ ├── message_repo.go │ ├── usecase/ │ │ ├── message_service.go │── migrations/ │ ├── 001_create_messages_table.sql │── .env │── go.mod │── go.sum |
1. Подключаем PostgreSQL
Файл config/config.go
Добавляем параметры БД
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package config import ( "log" "os" "github.com/joho/godotenv" ) type Config struct { TelegramToken string DatabaseURL string Debug bool } func LoadConfig() *Config { if err := godotenv.Load(); err != nil { log.Println("⚠ .env файл не найден, используем переменные окружения") } return &Config{ TelegramToken: getEnv("TELEGRAM_TOKEN", ""), DatabaseURL: getEnv("DATABASE_URL", ""), Debug: getEnv("DEBUG", "false") == "true", } } func getEnv(key, defaultValue string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultValue } |
2. Подключение к БД
Файл internal/infrastructure/database/postgres.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package database import ( "database/sql" "log" _ "github.com/lib/pq" ) func ConnectDB(databaseURL string) (*sql.DB, error) { db, err := sql.Open("postgres", databaseURL) if err != nil { return nil, err } if err := db.Ping(); err != nil { return nil, err } log.Println("✅ Подключение к БД успешно!") return db, nil } |
3. Миграция для создания таблицы
Создаём файл migrations/001_create_messages_table.sql
:
1 2 3 4 5 6 7 |
CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, user_id BIGINT NOT NULL, text TEXT NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); |
1 2 |
psql $DATABASE_URL -f migrations/001_create_messages_table.sql |
4. Репозиторий для работы с БД
Файл internal/repository/message_repo.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
package repository import ( "database/sql" "log" "telegram-bot/internal/domain" ) type MessageRepository struct { db *sql.DB } func NewMessageRepository(db *sql.DB) *MessageRepository { return &MessageRepository{db: db} } func (r *MessageRepository) SaveMessage(msg domain.Message) error { _, err := r.db.Exec("INSERT INTO messages (user_id, text) VALUES ($1, $2)", msg.UserID, msg.Text) if err != nil { log.Println("Ошибка при сохранении сообщения:", err) } return err } func (r *MessageRepository) GetMessages() ([]domain.Message, error) { rows, err := r.db.Query("SELECT id, user_id, text FROM messages") if err != nil { return nil, err } defer rows.Close() var messages []domain.Message for rows.Next() { var msg domain.Message if err := rows.Scan(&msg.ID, &msg.UserID, &msg.Text); err != nil { return nil, err } messages = append(messages, msg) } return messages, nil } |
5. Сервис обработки сообщений
Файл internal/usecase/message_service.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package usecase import ( "fmt" "telegram-bot/internal/domain" "telegram-bot/internal/repository" ) type MessageService struct { repo *repository.MessageRepository } func NewMessageService(repo *repository.MessageRepository) *MessageService { return &MessageService{repo: repo} } func (s *MessageService) ProcessMessage(msg domain.Message) string { if msg.Text == "/start" { return "Привет! Я твой бот." } if msg.Text == "/history" { messages, err := s.repo.GetMessages() if err != nil { return "Ошибка при получении истории сообщений." } response := "История сообщений:\n" for _, m := range messages { response += fmt.Sprintf("- %s\n", m.Text) } return response } _ = s.repo.SaveMessage(msg) return fmt.Sprintf("Ты сказал: %s", msg.Text) } |
6. Telegram Bot с Webhook
Файл internal/infrastructure/telegram/bot.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package telegram import ( "log" "net/http" "telegram-bot/internal/domain" "telegram-bot/internal/usecase" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) type Bot struct { api *tgbotapi.BotAPI messageService *usecase.MessageService } func NewBot(api *tgbotapi.BotAPI, messageService *usecase.MessageService) *Bot { return &Bot{ api: api, messageService: messageService, } } func (b *Bot) StartWebhook(webhookURL string) { _, err := b.api.SetWebhook(tgbotapi.NewWebhook(webhookURL)) if err != nil { log.Fatalf("Ошибка установки Webhook: %v", err) } updates := b.api.ListenForWebhook("/") go http.ListenAndServe(":8080", nil) log.Println("✅ Бот слушает Webhook!") for update := range updates { if update.Message != nil { go b.handleMessage(update.Message) } } } func (b *Bot) handleMessage(msg *tgbotapi.Message) { log.Printf("[%s] %s", msg.From.UserName, msg.Text) message := domain.Message{ ID: msg.MessageID, Text: msg.Text, UserID: msg.From.ID, } responseText := b.messageService.ProcessMessage(message) reply := tgbotapi.NewMessage(msg.Chat.ID, responseText) b.api.Send(reply) } |
7. Входная точка cmd/bot/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package main import ( "database/sql" "log" "telegram-bot/config" "telegram-bot/internal/infrastructure/database" "telegram-bot/internal/infrastructure/telegram" "telegram-bot/internal/repository" "telegram-bot/internal/usecase" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) func main() { cfg := config.LoadConfig() db, err := database.ConnectDB(cfg.DatabaseURL) if err != nil { log.Fatalf("Ошибка подключения к БД: %v", err) } defer db.Close() bot, err := tgbotapi.NewBotAPI(cfg.TelegramToken) if err != nil { log.Fatalf("Ошибка при создании бота: %v", err) } repo := repository.NewMessageRepository(db) messageService := usecase.NewMessageService(repo) telegramBot := telegram.NewBot(bot, messageService) webhookURL := "https://your-server.com/webhook" telegramBot.StartWebhook(webhookURL) } |
Запуск
- Настроить
.env
- Выполнить миграции
- Запустить сервер
12go run cmd/bot/main.go
Теперь бот использует PostgreSQL, Webhook, goroutines и поддерживает команду /history
!
Recommended Posts
Golang map и Swiss Table
16.03.2025