package pf import ( "bytes" "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "time" ) type Telegram struct { token string client *http.Client } type tgResponse[T any] struct { OK bool `json:"ok"` Description string `json:"description"` Result T `json:"result"` } type TGUpdate struct { UpdateID int64 `json:"update_id"` Message *TGMessage `json:"message"` } type TGMessage struct { MessageID int64 `json:"message_id"` Text string `json:"text"` Chat TGChat `json:"chat"` From *TGUser `json:"from"` } type TGChat struct { ID int64 `json:"id"` } type TGUser struct { ID int64 `json:"id"` Username string `json:"username"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } func NewTelegram(token string) *Telegram { return &Telegram{token: token, client: &http.Client{Timeout: 35 * time.Second}} } func (t *Telegram) Enabled() bool { return strings.TrimSpace(t.token) != "" } func (t *Telegram) SendMessage(ctx context.Context, chatID string, text string) error { if !t.Enabled() { return nil } payload := map[string]any{ "chat_id": chatID, "text": text, "parse_mode": "HTML", "disable_web_page_preview": false, } body, _ := json.Marshal(payload) req, err := http.NewRequestWithContext(ctx, http.MethodPost, t.apiURL("sendMessage"), bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := t.client.Do(req) if err != nil { return err } defer resp.Body.Close() var out tgResponse[json.RawMessage] if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return err } if !out.OK { return errors.New(out.Description) } return nil } func (t *Telegram) GetUpdates(ctx context.Context, offset int64) ([]TGUpdate, error) { if !t.Enabled() { return nil, fmt.Errorf("TG_BOT_TOKEN не задан в k8s/secrets.yaml") } values := url.Values{} values.Set("timeout", "25") if offset > 0 { values.Set("offset", fmt.Sprintf("%d", offset)) } req, err := http.NewRequestWithContext(ctx, http.MethodGet, t.apiURL("getUpdates")+"?"+values.Encode(), nil) if err != nil { return nil, err } resp, err := t.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var out tgResponse[[]TGUpdate] if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return nil, err } if !out.OK { return nil, errors.New(out.Description) } return out.Result, nil } func (t *Telegram) apiURL(method string) string { return "https://api.telegram.org/bot" + t.token + "/" + method } func (u TGUser) FullName() string { name := strings.TrimSpace(strings.TrimSpace(u.FirstName) + " " + strings.TrimSpace(u.LastName)) if name != "" { return name } if u.Username != "" { return u.Username } return fmt.Sprintf("user_%d", u.ID) }