8.5 KiB
gw_telegram
Bramka Telegram do n8n i systemów agentów AI. Bot przyjmuje wiadomości tekstowe i głosowe od użytkowników i routuje je do odpowiednich workflow w n8n lub agentów AI.
Funkcje
- Wiadomości tekstowe — routing oparty na regułach regex do dowolnego workflow n8n
- Wiadomości głosowe — pipeline: pobieranie OGG → konwersja ffmpeg (WAV 16kHz) → transkrypcja Whisper API → routing jak tekst
- Sesje użytkowników — historia konwersacji przechowywana w Redis z konfigurowalnym TTL
- Graceful shutdown — obsługa sygnałów
SIGINT/SIGTERM, bot kończy obsługę aktualnych wiadomości przed zatrzymaniem - Tryby pracy — polling (domyślny) lub webhook
Architektura
Projekt stosuje Clean Architecture z pełnym oddzieleniem warstw:
cmd/bot/main.go # composition root — łączy wszystkie warstwy
internal/
domain/ # logika biznesowa, zero zależności zewnętrznych
entity/ # modele: Message, Session, Intent, Route, WorkflowRequest
port/ # interfejsy (bramy zależności)
apperror/ # typowane błędy domenowe
application/ # przypadki użycia
usecase/ # HandleTextMessage, HandleVoiceMessage
dto/ # obiekty transferu danych
infrastructure/ # implementacje portów
telegram/ # BotGateway, UpdatePoller, FileDownloader
n8n/ # WebhookDispatcher
speech/ # OpenAIWhisper, FFmpegConverter
router/ # RuleBasedRouter
storage/ # RedisSessionStore
interfaces/ # warstwa wejściowa
telegram_handler.go # mapowanie update → use case
health_handler.go # endpointy /health i /ready
config/ # ładowanie konfiguracji z env
test/testutil/ # fake'i dla wszystkich portów (TDD)
deploy/ # Dockerfile, docker-compose.yml
Pipeline wiadomości głosowej
Telegram Voice Update
│
▼
TelegramHandler.Handle()
│
▼
HandleVoiceMessage.Execute()
1. SendTyping (wskaźnik pisania)
2. FileDownloader.Download(fileID) → OGG Opus bytes
3. FFmpegConverter.Convert() → WAV 16kHz mono
4. OpenAIWhisper.Transcribe() → tekst
│
▼
HandleTextMessage.Execute() → routing → n8n → odpowiedź
Kontrakt JSON z n8n
Bot wysyła do webhooka n8n:
{
"request_id": "uuid-v4",
"chat_id": 123456789,
"user_id": 987654321,
"username": "jankowalski",
"message_text": "Gdzie jest moje zamówienie #12345?",
"intent_name": "order_inquiry",
"timestamp": "2026-04-16T10:00:00Z",
"metadata": {
"route_target_type": "n8n"
}
}
n8n odpowiada:
{
"reply": "Twoje zamówienie #12345 jest w drodze.",
"actions": [],
"next_workflow": null
}
Jeśli n8n zwróci surowy tekst (nie JSON), bot wyśle go bezpośrednio do użytkownika.
Wymagania
- Go 1.22+
- Redis 7+
- ffmpeg (wymagany do obsługi wiadomości głosowych)
- Task (
go install github.com/go-task/task/v3/cmd/task@latest) - Token bota Telegram (@BotFather)
- Klucz API OpenAI (do transkrypcji głosu)
- Działająca instancja n8n
Konfiguracja
Konfiguracja odbywa się wyłącznie przez zmienne środowiskowe. Skopiuj .env.example jako punkt startowy:
cp .env.example .env
Zmienne środowiskowe
Bot Telegram (wymagane)
| Zmienna | Domyślna | Opis |
|---|---|---|
TELEGRAM_BOT_TOKEN |
— | Token bota z @BotFather |
BOT_MODE |
polling |
Tryb pracy: polling lub webhook |
TELEGRAM_WEBHOOK_URL |
— | URL webhooka (tylko dla BOT_MODE=webhook) |
TELEGRAM_DEBUG |
false |
Włącza szczegółowe logi Telegram API |
n8n (wymagane)
| Zmienna | Domyślna | Opis |
|---|---|---|
N8N_BASE_URL |
— | Bazowy URL instancji n8n, np. http://localhost:5678 |
N8N_AUTH_TOKEN |
— | Token Bearer do uwierzytelniania webhooka |
N8N_TIMEOUT |
30 |
Timeout żądania do n8n (sekundy) |
N8N_RETRY_COUNT |
3 |
Liczba ponownych prób przy błędzie |
Speech-to-Text
| Zmienna | Domyślna | Opis |
|---|---|---|
STT_PROVIDER |
openai |
Dostawca transkrypcji: openai |
OPENAI_API_KEY |
— | Klucz API OpenAI (wymagany jeśli STT_PROVIDER=openai) |
WHISPER_MODEL |
whisper-1 |
Model Whisper |
WHISPER_LANGUAGE |
— | Kod języka BCP-47 (np. pl, en). Puste = autodetekcja |
FFMPEG_PATH |
ffmpeg |
Ścieżka do binarki ffmpeg |
Redis
| Zmienna | Domyślna | Opis |
|---|---|---|
REDIS_URL |
redis://localhost:6379 |
URL połączenia z Redis |
SESSION_TTL |
24 |
Czas życia sesji użytkownika (godziny) |
Serwer HTTP
| Zmienna | Domyślna | Opis |
|---|---|---|
SERVER_PORT |
8080 |
Port serwera HTTP (/health, /ready) |
Logowanie
| Zmienna | Domyślna | Opis |
|---|---|---|
LOG_LEVEL |
info |
Poziom logów: debug, info, warn, error |
LOG_FORMAT |
json |
Format logów: json lub text |
Uruchomienie
Lokalne (bez Dockera)
Wymagania: Go 1.22+, Redis, ffmpeg zainstalowane lokalnie.
# 1. Sklonuj i przejdź do katalogu
git clone <repo-url>
cd gw_telegram
# 2. Pobierz zależności
go mod download
# 3. Skonfiguruj zmienne środowiskowe
cp .env.example .env
# edytuj .env — uzupełnij TELEGRAM_BOT_TOKEN, N8N_BASE_URL, OPENAI_API_KEY
# 4. Eksportuj zmienne
export $(grep -v '^#' .env | xargs)
# 5. Uruchom
task run
Docker Compose (zalecane)
# 1. Skonfiguruj zmienne
cp .env.example .env
# uzupełnij .env
# 2. Zbuduj i uruchom
task docker:up
# 3. Sprawdź logi
docker compose -f deploy/docker-compose.yml logs -f bot
# 4. Zatrzymaj
task docker:down
Docker Compose uruchamia bota razem z Redisem. Redis automatycznie persystuje dane sesji w woluminie redis_data.
Binarka produkcyjna
task build
# binarka: ./bin/bot
TELEGRAM_BOT_TOKEN=xxx N8N_BASE_URL=http://n8n:5678 ./bin/bot
Routing wiadomości
Routing jest oparty na regułach regex z priorytetem. Reguły są definiowane w kodzie (cmd/bot/main.go) i dopasowywane od najwyższego priorytetu. Pierwsza pasująca reguła wygrywa.
Domyślne reguły
| Pattern | Intent | Target | Priorytet |
|---|---|---|---|
^/start |
start |
builtin | 100 |
^/help |
help |
builtin | 100 |
.* |
general_query |
n8n: default |
0 |
Dodawanie nowego workflow n8n
- Dodaj regułę w
cmd/bot/main.go:
{
Pattern: regexp.MustCompile(`(?i)zamówienie|order`),
IntentName: "order_inquiry",
Target: entity.RouteTarget{
Type: entity.RouteTargetN8n,
WorkflowID: "order-webhook",
},
Priority: 50,
},
- Dodaj konfigurację workflow w mapie
workflows:
workflows := map[string]n8n.WorkflowConfig{
"default": {
WebhookURL: cfg.N8n.BaseURL + "/webhook/default",
AuthToken: cfg.N8n.AuthToken,
},
"order-webhook": {
WebhookURL: cfg.N8n.BaseURL + "/webhook/order-inquiry",
AuthToken: cfg.N8n.AuthToken,
},
}
Testy
# Testy jednostkowe
task test
# Testy z raportem pokrycia (HTML)
task test:cover
# otwórz coverage.html
# Testy integracyjne (wymaga Dockera)
task test:int
Projekt stosuje TDD. Każdy przypadek użycia ma testy oparte na ręcznie pisanych fake'ach portów (test/testutil/), bez generowanych mocków.
Pokrycie testami
| Pakiet | Testowane scenariusze |
|---|---|
HandleTextMessage |
Happy path, pusty tekst, brak trasy, błąd dispatchera, persystencja sesji |
HandleVoiceMessage |
Happy path, błąd pobierania pliku, błąd transkrypcji, konwersja audio |
Linting
task lint
Projekt używa golangci-lint. Konfiguracja w .golangci.yml.
Health Check
Serwer HTTP (SERVER_PORT) udostępnia:
GET /health— zawsze zwraca200 OKjeśli proces działaGET /ready— zwraca200 OKgdy połączenie z Telegram API i Redis jest aktywne
Przydatne do probes w Kubernetes i healthcheck w Docker Compose.
Zależności
| Biblioteka | Zastosowanie |
|---|---|
go-telegram-bot-api/v5 |
Telegram Bot API (polling + wysyłanie wiadomości) |
kelseyhightower/envconfig |
Ładowanie konfiguracji ze zmiennych środowiskowych |
redis/go-redis/v9 |
Klient Redis do przechowywania sesji |
google/uuid |
Generowanie RequestID |
stretchr/testify |
Asercje w testach |
log/slog (stdlib) |
Strukturalne logowanie JSON |