package telegram import ( "context" "fmt" "io" "net/http" "net/url" "strings" "time" ) // Sender abstracts the actual Telegram Bot API call so the service layer // can be unit-tested without making real network requests. type Sender interface { Send(ctx context.Context, botToken, chatID, text string) error } type httpSender struct { client *http.Client } func NewHTTPSender() Sender { return &httpSender{client: &http.Client{Timeout: 10 * time.Second}} } func (s *httpSender) Send(ctx context.Context, botToken, chatID, text string) error { if botToken == "" || chatID == "" { return fmt.Errorf("telegram bot token and chat id must be configured") } endpoint := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken) form := url.Values{ "chat_id": {chatID}, "text": {text}, } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, strings.NewReader(form.Encode())) if err != nil { return fmt.Errorf("build telegram request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, err := s.client.Do(req) if err != nil { return fmt.Errorf("call telegram api: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) return fmt.Errorf("telegram api returned status %d: %s", resp.StatusCode, string(body)) } return nil }