Files
gateway-telegram/README.md

305 lines
8.5 KiB
Markdown

# 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:
```json
{
"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:
```json
{
"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](https://taskfile.dev) (`go install github.com/go-task/task/v3/cmd/task@latest`)
- Token bota Telegram ([@BotFather](https://t.me/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:
```bash
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.
```bash
# 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)
```bash
# 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
```bash
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
1. Dodaj regułę w `cmd/bot/main.go`:
```go
{
Pattern: regexp.MustCompile(`(?i)zamówienie|order`),
IntentName: "order_inquiry",
Target: entity.RouteTarget{
Type: entity.RouteTargetN8n,
WorkflowID: "order-webhook",
},
Priority: 50,
},
```
2. Dodaj konfigurację workflow w mapie `workflows`:
```go
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
```bash
# 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
```bash
task lint
```
Projekt używa `golangci-lint`. Konfiguracja w `.golangci.yml`.
## Health Check
Serwer HTTP (`SERVER_PORT`) udostępnia:
- `GET /health` — zawsze zwraca `200 OK` jeśli proces działa
- `GET /ready` — zwraca `200 OK` gdy 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 |