mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
127 lines
3.6 KiB
Go
127 lines
3.6 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const defaultTelegramAPIBaseURL = "https://api.telegram.org"
|
|
|
|
var telegramTokenPattern = regexp.MustCompile(`bot[0-9]+:[^/\s"'?]+`)
|
|
|
|
func telegramAPIBaseURL(cfg map[string]string) string {
|
|
base := strings.TrimSpace(cfg["api_base_url"])
|
|
if base == "" {
|
|
base = strings.TrimSpace(os.Getenv("MEDIASTATION_TELEGRAM_API_BASE_URL"))
|
|
}
|
|
if base == "" {
|
|
base = defaultTelegramAPIBaseURL
|
|
}
|
|
return strings.TrimRight(base, "/")
|
|
}
|
|
|
|
func telegramMethodURL(cfg map[string]string, botToken, method string) (string, error) {
|
|
botToken = strings.TrimSpace(botToken)
|
|
method = strings.TrimSpace(method)
|
|
if botToken == "" {
|
|
return "", errors.New("telegram bot_token required")
|
|
}
|
|
if method == "" {
|
|
return "", errors.New("telegram method required")
|
|
}
|
|
base := telegramAPIBaseURL(cfg)
|
|
if _, err := url.ParseRequestURI(base); err != nil {
|
|
return "", fmt.Errorf("telegram api_base_url invalid")
|
|
}
|
|
return fmt.Sprintf("%s/bot%s/%s", base, botToken, method), nil
|
|
}
|
|
|
|
func telegramHTTPClient(timeout time.Duration, cfg map[string]string) *http.Client {
|
|
transport := NewExternalTransport()
|
|
proxyRaw := strings.TrimSpace(cfg["proxy_url"])
|
|
if proxyRaw == "" {
|
|
proxyRaw = strings.TrimSpace(os.Getenv("MEDIASTATION_TELEGRAM_PROXY_URL"))
|
|
}
|
|
if proxyRaw != "" {
|
|
if proxyURL, err := normalizeProxyURL(proxyRaw, "http"); err == nil {
|
|
transport.Proxy = http.ProxyURL(proxyURL)
|
|
}
|
|
}
|
|
return &http.Client{Timeout: timeout, Transport: transport}
|
|
}
|
|
|
|
func telegramPostForm(ctx context.Context, cfg map[string]string, method string, form url.Values, timeout time.Duration) error {
|
|
apiURL, err := telegramMethodURL(cfg, cfg["bot_token"], method)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, strings.NewReader(form.Encode()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
return telegramDo(telegramHTTPClient(timeout, cfg), req)
|
|
}
|
|
|
|
func telegramPostJSON(ctx context.Context, cfg map[string]string, method string, payload any, timeout time.Duration) error {
|
|
apiURL, err := telegramMethodURL(cfg, cfg["bot_token"], method)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
body, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, strings.NewReader(string(body)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
return telegramDo(telegramHTTPClient(timeout, cfg), req)
|
|
}
|
|
|
|
func telegramDo(client *http.Client, req *http.Request) error {
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return sanitizeTelegramError(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 400 {
|
|
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
|
return fmt.Errorf("telegram api error %d: %s", resp.StatusCode, sanitizeTelegramText(string(body)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func telegramStringConfigFromAny(cfg map[string]any) map[string]string {
|
|
out := make(map[string]string, len(cfg))
|
|
for key, value := range cfg {
|
|
out[key] = str(value)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func sanitizeTelegramError(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
msg := sanitizeTelegramText(err.Error())
|
|
if strings.Contains(msg, "Client.Timeout exceeded") || strings.Contains(msg, "context deadline exceeded") {
|
|
return errors.New("telegram request timeout: 请检查 NAS/Docker 到 Telegram API 的代理、反代或网络连通性")
|
|
}
|
|
return errors.New(msg)
|
|
}
|
|
|
|
func sanitizeTelegramText(text string) string {
|
|
return telegramTokenPattern.ReplaceAllString(text, "bot<redacted>")
|
|
}
|