# 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 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 |