Files
monitoring-pf/internal/pf/telegram.go
2026-06-05 10:18:42 +03:00

130 lines
2.9 KiB
Go

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)
}